Anyone who can write an APL function should be able to host it on the web.™
Rendered webpage |
APL code |
:class Tic_Tac_Toe :MiPageSample
⍝ Control:: _html.svg _DC.Table _DC.StyleSheet
⍝ Description:: Vector graphics Tic Tac Toe game
:field public STATE
:field public XOS
:field public BLANK
:field public NEXT
:field public WINS
:field public _Sessioned←1
∇ Compose;board;size;width;cells;data;game
:Access Public
Add _.title'TTT'
⍝ Set up the elements that will be used to draw with
B O X←New¨3/_.svg
'.O cx="50" cy="50" r="36"'O.Add _.circle
'.b'O.Add _.rect
'.X x1="11" y1="89" x2="89" y2="11"'X.Add _.line
'.X x1="11" y1="11" x2="89" y2="89"'X.Add _.line
'.b'X.Add _.rect
⍝ Prepare player marks
XOS←'X' 'O' ⍝ players
BLANK←' ' ⍝ empty cells
NEXT←⊃XOS ⍝ first player goes next
⍝ Prepare the board
size←5⌊3⌈⊃∊(//⎕VFI⍕Get'size'),3 ⍝ attempt to get board size from URL
ALL←,(∊'rc',¨⍕¨)¨⍳2/size ⍝ all cell refs; e.g. r1c1, r1c2, r1c3, r2c1, etc.
cells←(2/size)⍴⍳size*2 ⍝ cardinal number of ravelled cells
STATE←BLANK⊣¨,cells ⍝ initial board state
WINS←((1 1⍉⌽)⍪1 1∘⍉⍪⍉⍪⊢)cells ⍝ all winning index-combos; anti-diag, diag, cols, rows
⍝ Create container elements
game←'#game'Add _.div ⍝ container
'#title'game.Add _.h1'Tic Tac Toe' ⍝ game title
board←'#board'game.Add _.Table({B}¨cells) ⍝ table of blanks
board.MakeCellIds←1 ⍝ tableId_r1c1, tableId_c1c2, etc.
⍝ We want a single handler on the board, which is distributed to deeper <td>s (cells)
data←'cell' 'event' 'currentTarget.id' ⍝ 'cell' will be the id of the event target
Add _.Handler'#board' 'click' 'OnClick'data'' 'td' ⍝ event targets are all <td>s in #board
⍝ An area for output
'#next'game.Add _.p('Next: ',('.',NEXT)New _.span NEXT) ⍝ updated during the game
'#status'game.Add _.p'Go!' ⍝ for later messages
⍝ A button to start over at any point during a game
('#reset' '.button'game.Add _.button'Clear board').On'click' 'OnClick' ⍝ no data is needed to clear the board
⍝ Buttons to reload game with new size
{'.button'game.Add _.A(⍕⍵)('?size=',⍕⍵)}¨3 4 5~size
⍝ Add a styling
Add _.style'#game {width:',(⍕20+100×size),'px;}'
Add _.StyleSheet'/Examples/Data/Tic_Tac_Toe.css'
∇
∇ js←OnClick;reset;cell;clicked ⍝ Universal callback function
:Access public
js←'' ⍝ initiate JavaScript response
reset←'reset'≡_what ⍝ did user click Clear?
:If reset∨Won∨Tie ⍝ clear the board?
js,←'td[id^=''board_'']'Replace B ⍝ clear client cells (must use single quotes)
STATE[]←BLANK ⍝ clear server state
NEXT←⊃XOS ⍝ revert to first player
js,←0 Msg NEXT ⍝ update footer
js,←1 Msg'Board cleared' ⍝ and message
:EndIf
:If ~reset ⍝ did player either win or click cell?
cell←'#',Get'cell' ⍝ retrieve sent-along data
clicked←ALL⍳⊂7↓cell ⍝ cardinal number of chosen cell
:If BLANK≡clicked⌷STATE ⍝ is clicked cell available?
js,←cell Replace⍎NEXT ⍝ update the client cell
STATE[clicked]←⊂NEXT ⍝ update the server state
:If Won ⍝ do we have a win?
js,←0 Msg⊃XOS ⍝ update footer
js,←1 Msg NEXT,' wins' ⍝ update client status
:ElseIf Tie ⍝ no blanks left?
js,←0 Msg⊃XOS ⍝ update footer
js,←1 Msg'Tie game' ⍝ inform
:Else ⍝ regular move
NEXT←⊃(XOS⍳⊂NEXT)⌽XOS ⍝ rotate player stack to find next player
js,←0 Msg NEXT ⍝ update footer
:EndIf
:Else ⍝ clicked unavailable cell
js←1 Msg'That spot is occupied' ⍝ complain
:EndIf
:EndIf
∇
∇ won←Won ⍝ Did any player win?
won←1∊∧/XOS∘.≡STATE[WINS] ⍝ scalar boolean if any player won
∇
∇ tie←Tie ⍝ Is the board full?
tie←~BLANK∊STATE ⍝ no blanks in it
∇
∇ r←status Msg text ⍝ Various formats for displaying text in the output area
r←(~status)/'#next'Replace'Next: ',('.',text)New _.span text ⍝ show mark of next player?
r,←'#status'Replace('Go'text⊃⍨1+status),'!' ⍝ add message
∇
:endclass