井字遊戲
簡介
這是一個展示如何實作井字遊戲的範例。內容包含:
-
如何使用
amp-state
來維護遊戲狀態。 -
如何使用 *運算式* 來偵測獲勝的步驟。
-
如何使用 *動作* 和 *事件* 來建立互動式遊戲體驗。
設定
互動式遊戲體驗需要 amp-bind
。
<script async custom-element="amp-bind" src="https://cdn.ampproject.org/v0/amp-bind-0.1.js"></script>
初始遊戲狀態
我們使用一個名為 gameState
的 amp-state
來保存遊戲狀態。此外,有些狀態值未初始化,但會在下方說明
currentPlayer
:可以是 1 或 -1,分別代表 ⚡ 和 O。board
:儲存棋盤的狀態。此物件有 9 個屬性,從a
到i
,代表從左上到右下的方格。tr
、mr
、br
、lc
、mc
、rc
、fd
、bd
:分別代表**頂**列、**中**列等等,這 8 個值保存了朝向每個可能獲勝狀態的計數。
<amp-state id="gameState">
<script type="application/json">
{
"currentPlayer": 1,
"displayValues": {
"-1": "O",
"1": "⚡"
}
}
</script>
</amp-state>
顯示獲勝
為了偵測獲勝,我們會檢查是否有任何計數達到正數或負數 3。
開始玩吧!
⚡ 獲勝!!!
O 獲勝!!!
<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>
處理玩家的回合
遊戲棋盤上的每個方格都是一個按鈕。當玩家下棋時,狀態會更新
-
適當的計數會增加或減少。例如,下在左上角會改變 3 種可能獲勝狀態的計數:
tr
、lc
和bd
(頂列、左欄和反向對角線)。 -
board
的狀態會更新,以記錄此方格已被填滿。 -
透過將當前玩家乘以 `-1`,遊戲玩法會切換到另一位玩家。
每個方格的顯示屬性會根據狀態的變化而更新
-
[text]
:每個方格可能不包含任何內容,或包含來自board
的適當值。 -
[class]
:如果已達到此方格的任何獲勝狀態,則背景會相應地變更。 -
[disabled]
:如果已發生獲勝,或方格已被下過,則必須停用此按鈕。
<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>
重設遊戲狀態
若要開始新遊戲,board
和 8 個計數會重設為 null
,且 currentPlayer
會重設為 ⚡。
<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>
秘訣
將 ⚡ 放在中心方格!
需要進一步說明嗎?
如果此頁面上的說明未能涵蓋您的所有問題,請隨時與其他 AMP 使用者聯繫,討論您的具體使用案例。
前往 Stack Overflow 未說明的特色功能?AMP 專案非常鼓勵您的參與和貢獻!我們希望您能成為我們開源社群的長期參與者,同時我們也歡迎針對您特別關注的問題提供一次性貢獻。
在 GitHub 上編輯範例-
由 @garanj 撰寫