1
- import $ from 'jquery' ;
1
+ /**
2
+ * Handles game-play logic and player control.
3
+ */
4
+ export class Control {
5
+ /**
6
+ * Construct a control object and attach it to the root grid.
7
+ * @param {MetaGrid } rootGrid The root of a MetaGrid.
8
+ */
9
+ constructor ( rootGrid ) {
10
+ let _previousPlayer = 0 ;
2
11
3
- const ATTR_PLAYER_MARK = 'player-mark' ;
12
+ const currentPlayer = ( ) => {
13
+ return _previousPlayer ^= 1 ;
14
+ } ;
4
15
5
- export class Control {
6
- constructor ( metaGrid ) {
7
- let _currentPlayer = 0 ;
16
+ const applyToLeafs = ( grid , method ) => {
17
+ if ( grid . isLeaf ( ) ) {
18
+ method ( grid ) ;
19
+ return ;
20
+ }
21
+
22
+ for ( let row = 0 ; row < grid . getSize ( ) ; row ++ ) {
23
+ for ( let col = 0 ; col < grid . getSize ( ) ; col ++ ) {
24
+ applyToLeafs ( grid . getChild ( row , col ) , method ) ;
25
+ }
26
+ }
27
+ } ;
28
+
29
+ const checkRows = grid => {
30
+ for ( let row = 0 ; row < grid . getSize ( ) ; row ++ ) {
31
+ const firstCell = grid . getChild ( row , 0 ) . getMark ( ) ;
32
+ if ( firstCell == null )
33
+ continue ;
34
+
35
+ let col ;
36
+ for ( col = 0 ; col < grid . getSize ( ) ; col ++ ) {
37
+ if ( grid . getChild ( row , col ) . getMark ( ) !== firstCell )
38
+ break ;
39
+ }
40
+
41
+ if ( col === grid . getSize ( ) )
42
+ return true ;
43
+ }
44
+
45
+ return false ;
46
+ } ;
47
+
48
+ const checkColumns = grid => {
49
+ for ( let col = 0 ; col < grid . getSize ( ) ; col ++ ) {
50
+ const firstCell = grid . getChild ( 0 , col ) . getMark ( ) ;
51
+ if ( firstCell == null )
52
+ continue ;
53
+
54
+ let row ;
55
+ for ( row = 0 ; row < grid . getSize ( ) ; row ++ ) {
56
+ if ( grid . getChild ( row , col ) . getMark ( ) !== firstCell )
57
+ break ;
58
+ }
59
+
60
+ if ( row === grid . getSize ( ) )
61
+ return true ;
62
+ }
63
+
64
+ return false ;
65
+ } ;
66
+
67
+ const checkDiagonals = grid => {
68
+ const firstLeftCell = grid . getChild ( 0 , 0 ) . getMark ( ) ;
69
+ const firstRightCell = grid . getChild ( 0 , grid . getSize ( ) - 1 ) . getMark ( ) ;
70
+
71
+ let leftDiagonal = true , rightDiagonal = true ;
72
+ for ( let index = 0 ; index < grid . getSize ( ) ; index ++ ) {
73
+ if ( grid . getChild ( index , index ) . getMark ( ) !== firstLeftCell ) {
74
+ leftDiagonal = false ;
75
+ }
76
+
77
+ let mirrorIndex = grid . getSize ( ) - index - 1 ;
78
+ if ( grid . getChild ( index , mirrorIndex ) . getMark ( ) !== firstRightCell ) {
79
+ rightDiagonal = false ;
80
+ }
81
+ }
82
+ return firstLeftCell != null && leftDiagonal ||
83
+ firstRightCell != null && rightDiagonal ;
84
+ } ;
85
+
86
+ const checkWin = ( grid , checkMark ) => {
87
+ if ( grid . isMarked ( ) )
88
+ return ;
89
+
90
+ if ( checkRows ( grid ) || checkColumns ( grid ) || checkDiagonals ( grid ) ) {
91
+ grid . setMark ( checkMark ) ;
92
+
93
+ if ( ! grid . isRoot ( ) )
94
+ checkWin ( grid . getParent ( ) , checkMark ) ;
95
+ }
96
+ } ;
97
+
98
+ const countMarked = grid => {
99
+ let numMarked = 0 ;
100
+ for ( let row = 0 ; row < grid . getSize ( ) ; row ++ )
101
+ for ( let col = 0 ; col < grid . getSize ( ) ; col ++ )
102
+ if ( grid . getChild ( row , col ) . isMarked ( ) )
103
+ numMarked ++ ;
104
+ return numMarked ;
105
+ } ;
106
+
107
+ const isFilled = grid => {
108
+ const numChildren = grid . getSize ( ) * grid . getSize ( ) ;
109
+ return countMarked ( grid ) === numChildren ;
110
+ } ;
111
+
112
+ const findEmpty = grid => {
113
+ for ( let row = 0 ; row < grid . getSize ( ) ; row ++ ) {
114
+ for ( let col = 0 ; col < grid . getSize ( ) ; col ++ ) {
115
+ const child = grid . getChild ( row , col ) ;
116
+ if ( ! isFilled ( child ) ) {
117
+ return child ;
118
+ }
119
+ }
120
+ }
121
+ } ;
122
+
123
+ const enableReverse = ( grid , metaIndex ) => {
124
+ if ( metaIndex . length === 1 ) {
125
+ if ( isFilled ( grid ) && ! grid . isRoot ( ) )
126
+ grid = findEmpty ( grid . getParent ( ) ) ;
127
+
128
+ if ( grid == null )
129
+ return ;
130
+
131
+ applyToLeafs ( grid , ( leaf ) => {
132
+ leaf . enable ( true ) ;
133
+ } ) ;
134
+
135
+ return ;
136
+ }
137
+
138
+ const childIndex = metaIndex . pop ( ) ;
139
+ let child = grid . getChild ( childIndex . row , childIndex . col ) ;
140
+ enableReverse ( child , metaIndex ) ;
141
+ } ;
142
+
143
+ const handleClick = gridLeaf => {
144
+ const player = currentPlayer ( ) ;
145
+ if ( gridLeaf . isEnabled ( ) && ! gridLeaf . isMarked ( ) ) {
146
+ gridLeaf . setMark ( player ) ;
147
+ checkWin ( gridLeaf . getParent ( ) , player ) ;
148
+
149
+ applyToLeafs ( rootGrid , ( leaf ) => {
150
+ leaf . enable ( false ) ;
151
+ } ) ;
8
152
9
- function nextPlayer ( ) {
10
- _currentPlayer = 1 - _currentPlayer ;
11
- }
153
+ enableReverse ( rootGrid , gridLeaf . getMetaIndex ( ) ) ;
154
+ }
155
+ } ;
12
156
13
- metaGrid . onCellClick ( function ( cell ) {
14
- $ ( cell ) . attr ( ATTR_PLAYER_MARK , _currentPlayer ) ;
15
- nextPlayer ( ) ;
157
+ applyToLeafs ( rootGrid , ( leaf ) => {
158
+ leaf . getElement ( ) . onclick = ( ) => { handleClick ( leaf ) } ;
159
+ leaf . enable ( true ) ;
16
160
} ) ;
17
161
}
18
162
}
0 commit comments