Implement choice specification

This commit is contained in:
2021-04-04 22:45:15 -04:00
parent f28ac11427
commit 78edde8ca8
4 changed files with 186 additions and 132 deletions

View File

@@ -2,43 +2,29 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="stylesheet" href="%PUBLIC_URL%/pure-min.css" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
content="Web site helping you to make a decision"
/>
<link rel="stylesheet" href="https://unpkg.com/purecss@2.0.5/build/pure-min.css" integrity="sha384-G9DpmGxRIF6tpgbrkZVcZDeIomEU22LgTguPAI739bbKytjPE/kHTK5YxjJAAEXC" crossorigin="anonymous">
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
<title>Make a choice</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
<div id="main">
<div class="header">
<h1>Make a choice</h1>
</div>
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
<div class="content">
<div id="root"></div>
</div>
</div>
</body>
</html>

11
public/pure-min.css vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1,10 +1,41 @@
body {
font: 14px "Century Gothic", Futura, sans-serif;
margin: 20px;
color: #777;
}
ol, ul {
padding-left: 30px;
/*
The content `<div>` is where all your content goes.
*/
.content {
margin: 0 auto;
padding: 0 2em;
max-width: 800px;
margin-bottom: 50px;
line-height: 1.6em;
}
.header {
margin: 0;
color: #333;
text-align: center;
padding: 2.5em 2em 0;
border-bottom: 1px solid #eee;
}
.header h1 {
margin: 0.2em 0;
font-size: 3em;
font-weight: 300;
}
.header h2 {
font-weight: 300;
color: #ccc;
padding: 0;
margin-top: 0;
}
.content-subhead {
margin: 50px 0 20px 0;
font-weight: 300;
color: #888;
}
.board-row:after {
@@ -13,8 +44,8 @@ ol, ul {
display: table;
}
.status {
margin-bottom: 10px;
.space-1 {
margin-top: 1em;
}
.square {

View File

@@ -2,137 +2,163 @@ import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
function Square(props) {
return (
<button className="square" onClick={props.onClick}>
{props.value}
</button>
);
}
class Board extends React.Component {
renderSquare(i) {
return (
<Square
value={this.props.squares[i]}
onClick={() => this.props.onClick(i)}
/>
);
}
render() {
return (
<div>
<div className="board-row">
{this.renderSquare(0)}
{this.renderSquare(1)}
{this.renderSquare(2)}
</div>
<div className="board-row">
{this.renderSquare(3)}
{this.renderSquare(4)}
{this.renderSquare(5)}
</div>
<div className="board-row">
{this.renderSquare(6)}
{this.renderSquare(7)}
{this.renderSquare(8)}
</div>
</div>
);
function shuffleArray(array) {
/* https://stackoverflow.com/a/12646864 */
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
}
class Game extends React.Component {
class Decision {
constructor(choiceA, choiceB) {
this.choiceA = choiceA;
this.choiceB = choiceB;
this.result = null;
}
static createDecisions(choices) {
let decisions = [];
for (var i = 0; i < choices.length; i++) {
for (var j = 0; j < choices.length; j++) {
if (i !== j) {
let d = new Decision(choices[i], choices[j]);
decisions.push(d);
}
}
}
shuffleArray(decisions);
return decisions;
}
}
class Options extends React.Component {
constructor(props) {
super(props);
this.state = {
history: [{ squares: Array(9).fill(null) }],
stepNumber: 0,
xIsNext: true,
choicesText: null,
choices: [],
decisions: null,
warnings: null,
};
}
jumpTo(step) {
startChoosing() {
this.setState({
stepNumber: step,
xIsNext: step % 2 === 0,
decisions: Decision.createDecisions(this.state.choices),
});
}
handleClick(i) {
const history = this.state.history.slice(0, this.state.stepNumber + 1);
const current = history[history.length - 1];
const squares = current.squares.slice();
if (calculateWinner(squares) || squares[i]) {
return;
}
squares[i] = this.state.xIsNext ? "X" : "O";
choicesTextToChoices(choicesText) {
let choices = choicesText.split("\n");
choices = choices.filter((choice) => choice.length > 0);
return choices;
}
choicesOnChange(event) {
let choicesText = event.target.value;
let choices = this.choicesTextToChoices(choicesText);
this.setState({
history: history.concat([{ squares: squares }]),
stepNumber: history.length,
xIsNext: !this.state.xIsNext,
choicesText: choicesText,
choices: choices,
});
}
render() {
const history = this.state.history;
const current = history[this.state.stepNumber];
const winner = calculateWinner(current.squares);
const moves = history.map((step, move) => {
const desc = move ? "Go to move #" + move : "Go to game start";
renderChoosing() {
const decisions = this.state.decisions;
const decisionsToRender = decisions.map((decision, index) => {
return (
<li key={move}>
<button onClick={() => this.jumpTo(move)}>{desc}</button>
<li key={index}>
{decision.choiceA} - {decision.choiceB}
</li>
);
});
let status;
if (winner) {
status = "Winner " + winner;
} else {
status = "Next player: " + (this.state.xIsNext ? "X" : "O");
}
return (
<div className="game">
<div className="game-board">
<Board
squares={current.squares}
onClick={(i) => this.handleClick(i)}
/>
</div>
<div className="game-info">
<div>{status}</div>
<ol>{moves}</ol>
<div className="pure-g">
<div className="pure-u-1">
<ul>{decisionsToRender}</ul>
</div>
</div>
);
}
}
// ========================================
renderButton() {
let choices = this.state.choices;
let button = null;
if (choices.length > 1 && choices[1]) {
button = (
<button
className="pure-button pure-button-primary space-1"
onClick={() => this.startChoosing()}
>
Start choosing
</button>
);
} else {
button = (
<button className="pure-button pure-button-disabled space-1">
Start choosing
</button>
);
}
return button;
}
ReactDOM.render(<Game />, document.getElementById("root"));
renderChoiceInputField() {
return (
<div className="pure-u-1">
<h2 className="content-subhead">Type or paste your choices</h2>
<form className="pure-form" onSubmit={() => this.onSubmit}>
<fieldset className="pure-group">
<textarea
onChange={(event) => this.choicesOnChange(event)}
className="pure-input-1"
placeholder="Enter one choice per line"
></textarea>
</fieldset>
</form>
</div>
);
}
function calculateWinner(squares) {
const lines = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6],
];
for (let i = 0; i < lines.length; i++) {
const [a, b, c] = lines[i];
if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
return squares[a];
renderCurrentChoices() {
let choices = this.state.choices;
let choicesToRender = choices.map((choice, index) => {
return <li key={index}>{choice}</li>;
});
let button = this.renderButton();
return (
<div className="pure-u-1">
<h2 className="content-subhead">Your choices</h2>
<div className="pure-menu">
<ul>{choicesToRender}</ul>
</div>
<div>{button}</div>
</div>
);
}
renderSpecifyChoices() {
let input = this.renderChoiceInputField();
let output = this.renderCurrentChoices();
return (
<div className="pure-g">
{input}
{output}
</div>
);
}
render() {
if (!this.state.decisions) {
return this.renderSpecifyChoices();
} else {
return this.renderChoosing();
}
}
return null;
}
ReactDOM.render(<Options />, document.getElementById("root"));