Created
November 23, 2017 17:13
-
-
Save oieduardorabelo/084bd909a4107ccecfe1ee769db00bd7 to your computer and use it in GitHub Desktop.
Exemplo final para o artigo - Flow: Criando um Jogo da Velha, 2 de 2
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// @flow | |
import * as React from "react"; | |
import { render } from "react-dom"; | |
type Circle = { type: "Circle" }; | |
type Cross = { type: "Cross" }; | |
type Empty = { type: "Empty" }; | |
type CellType = Circle | Cross | Empty; | |
type Row = [CellType, CellType, CellType]; | |
type BoardType = [Row, Row, Row]; | |
type Player = 0 | 1; | |
type Just<A> = { type: "Just", result: A }; | |
type Nothing = { type: "Nothing" }; | |
type Maybe<A> = Just<A> | Nothing; | |
type Result = Maybe<[Player, Row]>; | |
type Status = Result | { type: "Running" }; | |
type State = { | |
board: BoardType, | |
player: Player, | |
status: Status | |
}; | |
const displayCell = (cell: CellType): string => { | |
switch (cell.type) { | |
case "Circle": | |
return "O"; | |
case "Cross": | |
return "X"; | |
default: | |
return ""; | |
} | |
}; | |
type CellProps = { | |
cell: CellType, | |
onClick: () => void | |
}; | |
const Cell = ({ cell, onClick }: CellProps) => { | |
return ( | |
<div | |
style={{ | |
float: "left", | |
fontSize: "3em", | |
border: "1px solid #eee", | |
height: "150px", | |
width: "150px", | |
textAlign: "center", | |
verticalAlign: "50%", | |
lineHeight: "150px" | |
}} | |
onClick={onClick} | |
> | |
{displayCell(cell)} | |
</div> | |
); | |
}; | |
type BoardProps = { | |
board: BoardType, | |
updateCell: (i: number) => void | |
}; | |
const Board = ({ board, updateCell }: BoardProps): React.Element<any> => { | |
return ( | |
<div> | |
{board.map((row, i) => { | |
return ( | |
<div style={{ width: "600px", height: "150px" }} key={i}> | |
{row.map((cell: CellType, j) => ( | |
<Cell key={j} cell={cell} onClick={() => updateCell(i * 3 + j)} /> | |
))} | |
</div> | |
); | |
})} | |
</div> | |
); | |
}; | |
// Helper functions | |
type ToFlatList = (list: BoardType) => Array<CellType>; | |
const toFlatList: ToFlatList = list => | |
list.reduce((xs, x) => { | |
return xs.concat(x); | |
}, []); | |
type ToBoard = (Array<CellType>) => BoardType; | |
const toBaord: ToBoard = ([c1, c2, c3, c4, c5, c6, c7, c8, c9]) => { | |
return [[c1, c2, c3], [c4, c5, c6], [c7, c8, c9]]; | |
}; | |
const pick = (selection: Array<number>, board: BoardType): Maybe<Row> => { | |
const flatlist: Array<CellType> = toFlatList(board); | |
const result = selection.reduce((xs, x) => { | |
const cell: ?CellType = flatlist[x]; | |
return cell ? [...xs, cell] : xs; | |
}, []); | |
if (result.length === 3) { | |
const [c1, c2, c3] = result; | |
return { type: "Just", result: [c1, c2, c3] }; | |
} | |
return { type: "Nothing" }; | |
}; | |
const validate = (row: Maybe<Row>): Player | null => { | |
if (row.type === "Nothing") return null; | |
const { result: [one, two, three] } = row; | |
if (one.type === two.type && one.type === three.type) { | |
return one.type === "Cross" ? 0 : one.type === "Circle" ? 1 : null; | |
} | |
return null; | |
}; | |
type IsWinner = (board: BoardType) => [Player, Row] | false; | |
const isWinner: IsWinner = (board, player) => { | |
const row1 = [0, 1, 2]; | |
const row2 = [3, 4, 5]; | |
const row3 = [6, 7, 8]; | |
const col1 = [0, 3, 6]; | |
const col2 = [1, 4, 7]; | |
const col3 = [2, 5, 8]; | |
const diag1 = [0, 4, 8]; | |
const diag2 = [2, 4, 6]; | |
const rows: Array<Array<number>> = [ | |
row1, | |
row2, | |
row3, | |
col1, | |
col2, | |
col3, | |
diag1, | |
diag2 | |
]; | |
return rows.reduce((selected, selection) => { | |
if (selected) return selected; | |
const row: Maybe<Row> = pick(selection, board); | |
if (row.type === "Nothing") return selected; | |
const winner = validate(row); | |
if (winner !== null) { | |
return [winner, row.result]; | |
} | |
return false; | |
}, false); | |
}; | |
const empty: Empty = { type: "Empty" }; | |
const emptyRow: Row = [empty, empty, empty]; | |
const board: BoardType = [emptyRow, emptyRow, emptyRow]; | |
const updateCell = ( | |
board: BoardType, | |
player: Player, | |
index: number | |
): BoardType => { | |
const cells = toFlatList(board); | |
const cell = cells[index]; | |
if (cell && cell.type === "Empty") { | |
const updatedCell: Circle | Cross = | |
player === 0 ? { type: "Cross" } : { type: "Circle" }; | |
return toBaord([ | |
...cells.slice(0, index), | |
updatedCell, | |
...cells.slice(index + 1) | |
]); | |
} | |
return board; | |
}; | |
const switchPlayer = (player: Player): Player => { | |
switch (player) { | |
case 0: | |
return 1; | |
case 1: | |
return 0; | |
default: | |
return 0; | |
} | |
}; | |
const isCell = (board: BoardType, index: number): boolean => { | |
const list = toFlatList(board); | |
return list[index] !== undefined; | |
}; | |
type IsFinished = (board: BoardType) => boolean; | |
const isFinished: IsFinished = board => | |
toFlatList(board).reduce((xs, x) => xs && x.type !== "Empty", true); | |
class TicTacToe extends React.Component<*, State> { | |
state = { | |
board: board, | |
status: { type: "Running" }, | |
player: 0 | |
}; | |
setCell = (index: number): void => { | |
this.setState(state => { | |
const { board, player } = state; | |
if (!isCell(board, index)) return {}; | |
const updatedBoard = updateCell(board, player, index); | |
const winner = isWinner(updatedBoard); | |
if (winner) { | |
return { | |
board: updatedBoard, | |
status: { type: "Just", result: winner } | |
}; | |
} else if (isFinished(updatedBoard)) { | |
return { | |
board: updatedBoard, | |
status: { type: "Nothing" } | |
}; | |
} else { | |
return { | |
board: updatedBoard, | |
player: switchPlayer(player) | |
}; | |
} | |
}); | |
}; | |
render() { | |
const { board, status } = this.state; | |
return ( | |
<div style={{ opacity: status.type === "Running" ? 1 : 0.3 }}> | |
<Board board={board} updateCell={this.setCell} /> | |
</div> | |
); | |
} | |
} | |
render(<TicTacToe />, document.getElementById("root")); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment