Files
flipper/desktop/plugins/public/rn-tic-tac-toe/index.tsx
Andrey Goncharov e639055fc4 Enable headless mode for the tic-tac-toe plugin
Reviewed By: passy

Differential Revision: D36517761

fbshipit-source-id: 0db933d825434da90e814054db531038ea93d610
2022-05-23 03:38:23 -07:00

203 lines
4.5 KiB
TypeScript

/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
import React from 'react';
import {Typography, Button, Alert, Space} from 'antd';
import {Draft} from 'immer';
import {
createState,
PluginClient,
usePlugin,
useValue,
styled,
theme,
Layout,
FlipperPluginInstance,
} from 'flipper-plugin';
type Player = ' ' | 'X' | 'O';
type State = {
cells: readonly [
Player,
Player,
Player,
Player,
Player,
Player,
Player,
Player,
Player,
];
winner: Player;
turn: 'X' | 'O';
};
function initialState(): State {
return {
// Cells
// 0 1 2
// 3 4 5
// 6 7 8
cells: [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '] as const,
turn: Math.random() < 0.5 ? 'O' : 'X',
winner: ' ',
} as const;
}
const computeNextState =
(cell: number, player: 'X' | 'O') => (draft: Draft<State>) => {
draft.cells[cell] = player;
draft.turn = player === 'X' ? 'O' : 'X';
draft.winner = computeWinner(draft.cells);
};
function computeWinner(c: State['cells']): Player {
// check the 2 diagonals
if ((c[0] === c[4] && c[0] === c[8]) || (c[2] === c[4] && c[2] === c[6])) {
return c[4];
}
for (let i = 0; i < 3; i++) {
// check vertical
if (c[i] === c[3 + i] && c[i] === c[6 + i]) {
return c[i];
}
// check horizontal
if (c[i * 3] === c[i * 3 + 1] && c[i * 3] === c[i * 3 + 2]) {
return c[i * 3];
}
}
return ' ';
}
type Events = {
XMove: {move: number};
GetState: never;
};
type Methods = {
SetState: (state: State) => Promise<void>;
};
export const plugin = (client: PluginClient<Events, Methods>) => {
const state = createState(initialState());
const sendUpdate = () => {
client.send('SetState', state.get());
};
const makeMove = (player: 'X' | 'O', move: number) => {
if (state.get().turn === player && state.get().cells[move] === ' ') {
state.update(computeNextState(move, player));
sendUpdate();
}
};
const reset = () => {
state.set(initialState());
sendUpdate();
};
client.onConnect(() => {
client.onMessage('XMove', ({move}) => {
makeMove('X', move);
});
client.onMessage('GetState', () => {
sendUpdate();
});
sendUpdate();
});
return {
makeMove,
reset,
state,
};
};
export const API = (pluginInstance: FlipperPluginInstance<typeof plugin>) => {
return {
makeMove: pluginInstance.makeMove,
reset: pluginInstance.reset,
state: pluginInstance.state,
};
};
const desktopPlayer = 'O';
export const Component = () => {
const pluginInstance = usePlugin(plugin);
const {winner, turn, cells} = useValue(pluginInstance.state);
return (
<Layout.Container>
<Space direction="vertical" align="center">
<Alert
message={
<>
This plugin demonstrates how to create pure JavaScript Flipper
plugins for React Native. Find out how to create a similar plugin
at{' '}
<a
href="https://fbflipper.com/docs/tutorial/intro"
target="blank">
fbflipper.com
</a>
.
</>
}
type="info"
/>
<Typography.Title>Flipper Tic-Tac-Toe</Typography.Title>
<Typography.Text>
{winner !== ' '
? `Winner! ${winner}`
: turn === 'O'
? 'Your turn'
: 'Mobile players turn..'}
</Typography.Text>
<GameBoard>
{cells.map((c, idx) => (
<Cell
key={idx}
disabled={c !== ' ' || turn != desktopPlayer || winner !== ' '}
onClick={() => pluginInstance.makeMove(desktopPlayer, idx)}>
{c}
</Cell>
))}
</GameBoard>
<Button onClick={() => pluginInstance.reset()}>Start new game</Button>
</Space>
</Layout.Container>
);
};
const GameBoard = styled('div')({
display: 'grid',
gridTemplateColumns: 'repeat(3, 80px)',
gridTemplateRows: 'repeat(3, 80px)',
justifyContent: 'center',
gap: 10,
});
const Cell = styled('button')({
padding: 20,
width: '100%',
height: '100%',
minWidth: 80,
fontSize: 24,
flex: 0,
borderRadius: 4,
backgroundColor: theme.backgroundDefault,
color: 'white',
':disabled': {
backgroundColor: theme.disabledColor,
},
});