Tic-Tac-Toe
Introduction
This is a sample showing how to implement Tic-Tac-Toe. This includes:
-
How to use
amp-state
to maintain the state of play. -
How to use expressions to detect winning moves.
-
How to use actions and events to create interative gameplay.
Setup
amp-bind
is required for interactive gameplay.
<script async custom-element="amp-bind" src="https://cdn.ampproject.org/v0/amp-bind-0.1.js"></script>
Initial game state
We use an amp-state
called gameState
to keep the state of play. Additionally, some state values are not initialized but are explained below:
currentPlayer
: Can be 1 or -1, for ⚡ and O respectively.board
: Stores the state of the board. This object has 9 properties, froma
toi
, for the tiles from top-left to bottom-right.tr
,mr
,br
,lc
,mc
,rc
,fd
,bd
: Read as Top Row, Middle Row, and so on, these 8 values hold the tallies towards each of the possible winning states.
<amp-state id="gameState">
<script type="application/json">
{
"currentPlayer": 1,
"displayValues": {
"-1": "O",
"1": "⚡"
}
}
</script>
</amp-state>
Showing a win
To detect a win, we check whether any of the tallies has reached positive or negative 3.
Let's play!
⚡ wins!!!
O wins!!!
<div class="results-component">
<h1 [class]="max(abs(tr),abs(br),abs(lc),abs(rc),abs(fd),abs(bd),abs(mc),abs(mr)) == 3 ? 'hide' : 'show'">Let's play!</h1>
<h1 [class]="max(tr,br,lc,rc,fd,bd,mc,mr) == 3 ? 'show' : 'hide'" class="hide">⚡ wins!!!</h1>
<h1 [class]="min(tr,br,lc,rc,fd,bd,mc,mr) == -3 ? 'show' : 'hide'" class="hide">O wins!!!</h1>
</div>
Handling a player's turn
Each tile of the playing board is a button. When a move is played, state is updated:
-
The appropriate tallies are incremented or decremented. For example, playing top-left alters the tally for 3 possible winning states:
tr
,lc
andbd
(top row, left column and backward diagonal). -
The state of the
board
is updated, to record that this tile has been filled. -
The game play is switched to the other play by multiplying the current player by
-1
.
Display attributes for each tile are updated based on this change in state:
-
[text]
: Each tile either contains nothing, or the appropriate value fromboard
. -
[class]
: If any of the winning states for this tile has been reached, the background is changed accordingly. -
[disabled]
: This button must be disabled if either a win has occurred, or the tile already played.
<div class="board-component">
<table>
<tr>
<td class="cell">
<button on="tap:AMP.setState({
tr: tr + gameState.currentPlayer,
lc: lc + gameState.currentPlayer,
bd: bd + gameState.currentPlayer,
board: {
a: gameState.displayValues[gameState.currentPlayer]
},
gameState: {
currentPlayer: gameState.currentPlayer * -1
}
})" [text]="board.a ? board.a : ''" [class]="max(abs(tr),abs(lc),abs(bd)) == 3 ? 'grid-button win' : 'grid-button in-play'" [disabled]="board.a || max(abs(tr),abs(br),abs(lc),abs(rc),abs(fd),abs(bd),abs(mc),abs(mr)) == 3" class="grid-button in-play">
</button>
</td>
<td class="cell cell-vert">
<button on="tap:AMP.setState({
tr: tr + gameState.currentPlayer,
mc: mc + gameState.currentPlayer,
board: {
b: gameState.displayValues[gameState.currentPlayer]
},
gameState: {
currentPlayer: gameState.currentPlayer * -1
}
})" [text]="board.b ? board.b : ''" [class]="max(abs(tr),abs(mc)) == 3 ? 'grid-button win' : 'grid-button in-play'" [disabled]="board.b || max(abs(tr),abs(br),abs(lc),abs(rc),abs(fd),abs(bd),abs(mc),abs(mr)) == 3" class="grid-button in-play">
</button>
</td>
<td class="cell">
<button on="tap:AMP.setState({
tr: tr + gameState.currentPlayer,
rc: rc + gameState.currentPlayer,
fd: fd + gameState.currentPlayer,
board: {
c: gameState.displayValues[gameState.currentPlayer]
},
gameState: {
currentPlayer: gameState.currentPlayer * -1
}
})" [text]="board.c ? board.c : ''" [class]="max(abs(tr),abs(rc),abs(fd)) == 3 ? 'grid-button win' : 'grid-button in-play'" [disabled]="board.c || max(abs(tr),abs(br),abs(lc),abs(rc),abs(fd),abs(bd),abs(mc),abs(mr)) == 3" class="grid-button in-play">
</button>
</td>
</tr>
<tr>
<td class="cell cell-horiz">
<button on="tap:AMP.setState({
mr: mr + gameState.currentPlayer,
lc: lc + gameState.currentPlayer,
board: {
d: gameState.displayValues[gameState.currentPlayer]
},
gameState: {
currentPlayer: gameState.currentPlayer * -1
}
})" [text]="board.d ? board.d : ''" [class]="max(abs(mr),abs(lc)) == 3 ? 'grid-button win' : 'grid-button in-play'" [disabled]="board.d || max(abs(tr),abs(br),abs(lc),abs(rc),abs(fd),abs(bd),abs(mc),abs(mr)) == 3" class="grid-button in-play">
</button>
</td>
<td class="cell cell-horiz cell-vert">
<button on="tap:AMP.setState({
mr: mr + gameState.currentPlayer,
mc: mc + gameState.currentPlayer,
fd: fd + gameState.currentPlayer,bd: bd + gameState.currentPlayer,
board: {
e: gameState.displayValues[gameState.currentPlayer]
},
gameState: {
currentPlayer: gameState.currentPlayer * -1
}
})" [text]="board.e ? board.e : ''" [class]="max(abs(mr),abs(mc),abs(fd),abs(bd)) == 3 ? 'grid-button win' : 'grid-button in-play'" [disabled]="board.e || max(abs(tr),abs(br),abs(lc),abs(rc),abs(fd),abs(bd),abs(mc),abs(mr)) == 3" class="grid-button in-play">
</button>
</td>
<td class="cell cell-horiz">
<button on="tap:AMP.setState({
mr: mr + gameState.currentPlayer,
rc: rc + gameState.currentPlayer,
board: {
f: gameState.displayValues[gameState.currentPlayer]
},
gameState: {
currentPlayer: gameState.currentPlayer * -1
}
})" [text]="board.f ? board.f : ''" [class]="max(abs(mr),abs(rc)) == 3 ? 'grid-button win' : 'grid-button in-play'" [disabled]="board.f || max(abs(tr),abs(br),abs(lc),abs(rc),abs(fd),abs(bd),abs(mc),abs(mr)) == 3" class="grid-button in-play">
</button>
</td>
</tr>
<tr>
<td class="cell">
<button on="tap:AMP.setState({
br: br + gameState.currentPlayer,
lc: lc + gameState.currentPlayer,
fd: fd + gameState.currentPlayer,
board: {
g: gameState.displayValues[gameState.currentPlayer]
},
gameState: {
currentPlayer: gameState.currentPlayer * -1
}
})" [text]="board.g ? board.g : ''" [class]="max(abs(br),abs(lc),abs(fd)) == 3 ? 'grid-button win' : 'grid-button in-play'" [disabled]="board.g || max(abs(tr),abs(br),abs(lc),abs(rc),abs(fd),abs(bd),abs(mc),abs(mr)) == 3" class="grid-button in-play">
</button>
</td>
<td class="cell cell-vert">
<button on="tap:AMP.setState({
br: br + gameState.currentPlayer,
mc: mc + gameState.currentPlayer,
board: {
h: gameState.displayValues[gameState.currentPlayer]
},
gameState: {
currentPlayer: gameState.currentPlayer * -1
}
})" [text]="board.h ? board.h : ''" [class]="max(abs(br),abs(mc)) == 3 ? 'grid-button win' : 'grid-button in-play'" [disabled]="board.h || max(abs(tr),abs(br),abs(lc),abs(rc),abs(fd),abs(bd),abs(mc),abs(mr)) == 3" class="grid-button in-play">
</button>
</td>
<td class="cell">
<button on="tap:AMP.setState({
br: br + gameState.currentPlayer,
rc: rc + gameState.currentPlayer,
bd: bd + gameState.currentPlayer,
board: {
i: gameState.displayValues[gameState.currentPlayer]
},
gameState: {
currentPlayer: gameState.currentPlayer * -1
}
})" [text]="board.i ? board.i : ''" [class]="max(abs(br),abs(rc), abs(bd)) == 3 ? 'grid-button win' : 'grid-button in-play'" [disabled]="board.i || max(abs(tr),abs(br),abs(lc),abs(rc),abs(fd),abs(bd),abs(mc),abs(mr)) == 3" class="grid-button in-play">
</button>
</td>
</tr>
</table>
</div>
Resetting the game state
To start a new game, the board
and 8 tallies are reset to null
and the currentPlayer
reset to ⚡
.
<div class="reset-component">
<button class="reset-button" on="tap:AMP.setState({
gameState: {
currentPlayer: 1,
displayValues: {
'-1': 'O',
'1': '⚡'
}
},
tr: null,
mr: null,
br: null,
lc: null,
mc: null,
rc: null,
fd: null,
bd: null,
board: null
})">
Restart game
</button>
</div>
Top tips
Put ⚡ in the center square!
Se as explicações nesta página não respondem a todas as suas perguntas, entre em contato com outros usuários de AMP para discutir seu caso de uso específico.
Ir para o Stack Overflow Falta explicar algum recurso?O projeto AMP incentiva fortemente sua participação e contribuições! Esperamos que você se torne um participante assíduo de nossa comunidade de código aberto, mas também agradecemos contribuições pontuais para problemas que você tenha particular interesse.
Editar amostra no GitHub-
Written by @garanj