Skip to content

Instantly share code, notes, and snippets.

@oieduardorabelo
Created November 23, 2017 17:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save oieduardorabelo/084bd909a4107ccecfe1ee769db00bd7 to your computer and use it in GitHub Desktop.
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
// @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