()
| 3 | import { type Dir, getGamObjectAtPos, snakeMachine } from './snakeMachine'; |
| 4 | |
| 5 | function App() { |
| 6 | const [current, send] = useActor(snakeMachine); |
| 7 | const { gridSize, score, highScore } = current.context; |
| 8 | const isGameOver = current.matches('Game Over'); |
| 9 | console.log(current); |
| 10 | |
| 11 | useEffect(() => { |
| 12 | function keyListener(event: KeyboardEvent) { |
| 13 | const [maybeKey, maybeDir] = event.key.split('Arrow'); |
| 14 | if (maybeDir) { |
| 15 | send({ type: 'ARROW_KEY', dir: maybeDir as Dir }); |
| 16 | } else if (maybeKey === 'r') { |
| 17 | send({ type: 'NEW_GAME' }); |
| 18 | } |
| 19 | } |
| 20 | |
| 21 | window.addEventListener('keydown', keyListener); |
| 22 | return () => window.removeEventListener('keydown', keyListener); |
| 23 | }, [send]); |
| 24 | |
| 25 | return ( |
| 26 | <div className="App"> |
| 27 | <header> |
| 28 | <h1 style={{ marginBottom: 0 }}>XSnake</h1> |
| 29 | <p style={{ margin: 0 }}>Snake with a sweet twist, built with XState</p> |
| 30 | </header> |
| 31 | <p style={{ fontSize: '1.2em', marginBottom: 0 }}> |
| 32 | {isGameOver ? 'Game Over!' : '\u00A0'} |
| 33 | </p> |
| 34 | <p> |
| 35 | Score: {score} |
| 36 | <br /> |
| 37 | High score: {highScore} |
| 38 | </p> |
| 39 | <div |
| 40 | className="grid" |
| 41 | style={{ |
| 42 | gridTemplateColumns: `repeat(${gridSize.x}, 1fr)`, |
| 43 | gridTemplateRows: `repeat(${gridSize.y}, 1fr)` |
| 44 | }} |
| 45 | > |
| 46 | {Array.from({ length: gridSize.y }).map((_, row) => |
| 47 | Array.from({ length: gridSize.x }).map((_, col) => { |
| 48 | const { type, dir } = |
| 49 | getGamObjectAtPos(current.context, { x: col, y: row }) || {}; |
| 50 | return ( |
| 51 | <div className="cell" key={`${col} ${row}`}> |
| 52 | <span |
| 53 | role="img" |
| 54 | aria-label={type} |
| 55 | className={type} |
| 56 | data-dir={dir} |
| 57 | /> |
| 58 | </div> |
| 59 | ); |
| 60 | }) |
| 61 | )} |
| 62 | </div> |
nothing calls this directly
no test coverage detected