1+ <!DOCTYPE html>
2+ < html >
3+ < head >
4+ < meta charset ="utf-8 ">
5+ < link rel ="icon " href ="chomp.ico " type ="image/x-icon ">
6+ < title > Chomp</ title >
7+ < style >
8+ body {
9+ background-color : # 884820 ;
10+ left : 0 ;
11+ top : 0 ;
12+ margin : 0 ;
13+ width : 100% ;
14+ height : 100% ;
15+ position : absolute;
16+ font-family : Arial, Helvetica, sans-serif;
17+ color : chocolate;
18+ }
19+ # board {
20+ position : absolute;
21+ box-sizing : border-box;
22+ border : 2vmin solid chocolate;
23+ left : 50% ;
24+ top : 50% ;
25+ margin-left : -34vmin ;
26+ margin-top : -30vmin ;
27+ width : 68vmin ;
28+ height : 60vmin ;
29+ filter : blur (5px );
30+ }
31+ # menu {
32+ position : absolute;
33+ box-sizing : border-box;
34+ left : 50% ;
35+ top : 50% ;
36+ margin-left : -32vmin ;
37+ margin-top : -28vmin ;
38+ width : 64vmin ;
39+ height : 56vmin ;
40+ padding : 2vmin ;
41+ color : # e08030 ;
42+ background-color : rgba (0 , 0 , 0 , 0.2 );
43+ }
44+ h1 {
45+ text-align : center;
46+ font-size : 6vmin ;
47+ margin : 1vmin ;
48+ }
49+ p {
50+ font-size : 3vmin ;
51+ }
52+ # haxis {
53+ position : absolute;
54+ left : 50% ;
55+ bottom : 50% ;
56+ margin-left : -32vmin ;
57+ margin-bottom : 30vmin ;
58+ width : 64vmin ;
59+ height : 8vmin ;
60+ }
61+ # vaxis {
62+ position : absolute;
63+ top : 50% ;
64+ right : 50% ;
65+ margin-top : -28vmin ;
66+ margin-right : 34vmin ;
67+ width : 8vmin ;
68+ height : 56vmin ;
69+ }
70+ .hlabel {
71+ position : relative;
72+ display : inline-block;
73+ width : 8vmin ;
74+ font-size : 8vmin ;
75+ text-align : center;
76+ }
77+ .vlabel {
78+ position : relative;
79+ display : block;
80+ height : 8vmin ;
81+ font-size : 8vmin ;
82+ text-align : center;
83+ }
84+ .piece {
85+ background-color : rgb (80 , 30 , 1 );
86+ border-color : rgb (91 , 41 , 6 );
87+ border-width : 1vmin ;
88+ border-style : outset;
89+ box-sizing : border-box;
90+ width : 8vmin ;
91+ height : 8vmin ;
92+ display : inline-block;
93+ position : relative;
94+ vertical-align : top;
95+ transition : opacity 500ms , margin 500ms , color 250ms , border-color 250ms ;
96+ cursor : pointer;
97+ }
98+ .piece : hover , .piece : focus {
99+ background-color : rgb (90 , 40 , 11 );
100+ border-color : rgb (101 , 51 , 16 );
101+ }
102+ .piece : active {
103+ border-style : inset;
104+ }
105+ .taken {
106+ opacity : 0 ;
107+ margin-left : 4vmin ;
108+ margin-top : 4vmin ;
109+ margin-right : -4vmin ;
110+ margin-bottom : -4vmin ;
111+ cursor : unset;
112+ }
113+ # p00 {
114+ width : 8vmin ;
115+ height : 8vmin ;
116+ position : relative;
117+ display : inline-block;
118+ margin : 0 ;
119+ vertical-align : top;
120+ }
121+ # two , # one {
122+ width : 32vmin ;
123+ height : 12vmin ;
124+ padding : 3vmin 0 ;
125+ text-align : center;
126+ font-size : 3vmin ;
127+ position : absolute;
128+ display : inline-block;
129+ }
130+ # two {
131+ right : 50% ;
132+ }
133+ # one {
134+ left : 50% ;
135+ }
136+ # turn {
137+ position : absolute;
138+ top : 50% ;
139+ left : 50% ;
140+ width : 68vmin ;
141+ height : 8vmin ;
142+ margin-left : -34vmin ;
143+ margin-top : 30vmin ;
144+ font-size : 4vmin ;
145+ text-align : center;
146+ }
147+ </ style >
148+ </ head >
149+ < body >
150+ < div id ="board "> < div id ="p00 "> </ div > </ div >
151+ < div id ="vaxis "> </ div >
152+ < div id ="haxis "> < div class ="hlabel "> 0</ div > </ div >
153+ < div id ="menu ">
154+ < h1 id ="title "> Chomp</ h1 >
155+ < p id ="desc "> Chomp is a game played on a rectangular chocolate bar between two players. The top left piece is removed in advance. Each player takes turn eating a piece of chocolate, along with all pieces to the right and below it. Whoever eats the last piece of chocolate wins.</ p >
156+ < div class ="piece " id ="two "> Player vs Player</ div >
157+ < div class ="piece " id ="one "> Player vs Computer</ div >
158+ </ div >
159+ < div id ="turn "> </ div >
160+ < script type ="text/javascript ">
161+ const ppos = Uint16Array . of ( 383 , 703 , 863 , 943 , 983 , 1003 , 1013 , 1018 , 1247 , 1647 , 1719 , 1755 , 1773 , 1782 , 1851 , 1885 , 1895 , 1950 , 1977 , 2287 , 2423 , 3195 , 3261 , 3294 , 3303 , 3383 , 3437 , 3446 , 3485 , 3502 , 3548 , 3646 , 3705 , 3757 , 3917 , 4343 , 4539 , 4573 , 4590 , 4731 , 4925 , 5046 , 5084 , 6269 , 6334 , 6747 , 6774 , 6835 , 6892 , 6972 , 6995 , 7075 , 7262 , 7283 , 7323 , 7356 , 7382 , 7526 , 7596 , 7636 , 7656 , 7788 , 7846 , 7896 , 7908 , 7988 , 8006 , 8112 , 8443 , 8573 , 8893 , 8926 , 9342 , 9533 , 9837 , 9885 , 10073 , 10153 , 10193 , 12509 , 12526 , 12606 , 12665 , 12717 , 12861 , 12894 , 13014 , 13044 , 13110 , 13148 , 13228 , 13272 , 13430 , 13517 , 13532 , 13673 , 13684 , 13721 , 13752 , 13772 , 13884 , 13937 , 14025 , 14460 , 14518 , 14553 , 14678 , 14705 , 14733 , 14758 , 14950 , 14953 , 14998 , 15025 , 15201 , 15558 , 15585 , 15638 , 15665 , 15689 , 16009 , 16637 , 16830 , 17022 , 17646 , 18102 , 18140 , 18236 , 18262 , 18342 , 18870 , 18894 , 19038 , 19246 , 19292 , 19302 , 19350 , 19372 , 19412 , 19432 , 19566 , 19686 , 20124 , 20280 , 20300 , 20388 , 24828 , 24926 , 25774 , 25910 , 25934 , 26214 , 26254 , 26390 , 26742 , 26830 , 26926 , 29070 , 29230 , 29382 , 29750 , 29774 , 30870 , 31110 , 33022 ) ;
162+ let currPos = 383 , p1 = 'Player 1' , p2 = 'Player 2' , move = 1 , AImove = false ;
163+ //fetch('16x').then(res => res.arrayBuffer()).then(arr => ppos = new Uint32Array(arr)).then(() =>
164+ //console.log('Loaded 16x ppos'), () => console.warn('Failed to load 16x ppos'));
165+ for ( let i = 1 ; i < 56 ; ++ i ) {
166+ const p = document . createElement ( 'div' ) ;
167+ p . className = 'piece' ;
168+ p . tabIndex = 0 ;
169+ p . id = 'p' + i . toString ( 8 ) . padStart ( 2 , '0' ) ;
170+ board . appendChild ( p ) ;
171+ }
172+ for ( let i = 0 ; i < 7 ; ) {
173+ const v = document . createElement ( 'div' ) ;
174+ v . className = 'vlabel' ;
175+ v . textContent = i ;
176+ vaxis . appendChild ( v ) ;
177+ const h = document . createElement ( 'div' ) ;
178+ h . className = 'hlabel' ;
179+ h . textContent = ++ i ;
180+ haxis . appendChild ( h ) ;
181+ }
182+
183+ const propagateRight = ( x , y ) => {
184+ const elem = document . getElementById ( 'p' + y + x ) ;
185+ if ( elem . className == 'piece' ) {
186+ elem . className = 'piece taken' ;
187+ if ( x < 7 ) {
188+ propagateRight ( x + 1 , y ) ;
189+ }
190+ }
191+ } ;
192+ const start = ( ) => {
193+ reset ( ) ;
194+ board . style . filter = 'none' ;
195+ menu . hidden = true ;
196+ turn . textContent = p1 + '\'s turn' ;
197+ } , reset = ( ) => {
198+ const elem = board . getElementsByClassName ( 'taken' ) ;
199+ while ( elem . length ) {
200+ elem [ elem . length - 1 ] . className = 'piece' ;
201+ }
202+ currPos = 32640 ;
203+ }
204+ board . addEventListener ( 'click' , e => {
205+ if ( AImove ) return ;
206+ if ( e . target . className == 'piece' ) {
207+ let x = + e . target . id [ 2 ] , y = + e . target . id [ 1 ] ;
208+ take ( x , y ) ;
209+ if ( ( ~ move & 1 ) && p2 [ 0 ] == 'T' ) {
210+ AImove = true ;
211+ [ x , y ] = AI ( ) ;
212+ document . getElementById ( 'p' + y + x ) . focus ( ) ;
213+ setTimeout ( ( ) => { take ( x , y ) ; AImove = false ; } , 800 ) ;
214+ }
215+ }
216+ } , true ) ;
217+ two . addEventListener ( 'click' , e => {
218+ if ( currPos > 383 ) return ;
219+ p1 = 'Player 1' ;
220+ p2 = 'Player 2' ;
221+ start ( ) ;
222+ } ) ;
223+ one . addEventListener ( 'click' , e => {
224+ if ( currPos > 383 ) return ;
225+ p1 = 'The player' ;
226+ p2 = 'The computer' ;
227+ start ( ) ;
228+ } )
229+ const binSearch = pos => {
230+ let min = 0 , max = ppos . length - 1 , index , current ;
231+ while ( min <= max ) {
232+ index = min + max >> 1 ;
233+ current = ppos [ index ] ;
234+ if ( current < pos )
235+ min = index + 1 ;
236+ else if ( current > pos )
237+ max = index - 1 ;
238+ else
239+ return true ;
240+ }
241+ return false ;
242+ } , popcount = pos => {
243+ pos = pos - ( ( pos >> 1 ) & 0x5555 ) ;
244+ pos = ( pos & 0x3333 ) + ( ( pos >> 2 ) & 0x3333 ) ;
245+ pos = ( pos + ( pos >> 4 ) & 0x0f0f ) ;
246+ return ( pos + ( pos >> 8 ) ) & 31 ;
247+ } , reflect = pos => {
248+ pos = ( pos & 0x5555 ) << 1 | ( pos & 0xaaaa ) >> 1 ;
249+ pos = ( pos & 0x3333 ) << 2 | ( pos & 0xcccc ) >> 2 ;
250+ pos = ( pos & 0x0f0f ) << 4 | ( pos & 0xf0f0 ) >> 4 ;
251+ return ~ ( pos >> 8 | pos << 8 ) & 0xffff ;
252+ } , normalize = pos => Math . min ( pos , reflect ( pos ) ) ,
253+ AI = ( pos = currPos ) => {
254+ if ( binSearch ( normalize ( pos ) ) ) {
255+ const elm = Array . from ( board . querySelectorAll ( '.piece:not(.taken)' ) , e => [ + e . id [ 2 ] , + e . id [ 1 ] ] ) ;
256+ let filtered = elm . filter ( e => e [ 0 ] > 2 && e [ 1 ] > 2 ) ;
257+ if ( filtered . length )
258+ return filtered [ Math . random ( ) * filtered . length | 0 ] ;
259+ filtered = elm . filter ( e => {
260+ let next = npos ( pos , e [ 0 ] , e [ 1 ] ) ;
261+ if ( ! next )
262+ return false ;
263+ if ( next & 63 == 63 )
264+ return false ;
265+ if ( next < 1532 )
266+ return false ;
267+ let [ x , y ] = AI ( next ) ;
268+ if ( x == 1 && y == 1 )
269+ return false ;
270+ if ( x + y < 3 )
271+ return false ;
272+ return true ;
273+ } ) ;
274+ if ( filtered . length )
275+ return filtered [ Math . random ( ) * filtered . length | 0 ] ;
276+ let max = 0 , index = - 1 ;
277+ for ( let i = 0 ; i < elm . length ; ++ i ) {
278+ if ( elm [ i ] [ 0 ] + elm [ i ] [ 1 ] > max ) {
279+ max = elm [ i ] [ 0 ] + elm [ i ] [ 1 ] ;
280+ index = i ;
281+ }
282+ }
283+ if ( index > 2 )
284+ return elm [ index - Math . random ( ) * 3 | 0 ]
285+ return elm [ index ] ;
286+ }
287+ let ipos = pos ^ - 1 >>> Math . clz32 ( pos ) ;
288+ let y = 0 ;
289+ do {
290+ let body = ipos & - ipos ;
291+ ipos ^= body ;
292+ let tail = pos & body - 1 ;
293+ let head = pos ^ tail ;
294+ do {
295+ head &= head - 1 ;
296+ tail |= body ;
297+ if ( binSearch ( normalize ( head | tail ) ) ) {
298+ return [ popcount ( head ) , y ] ;
299+ }
300+ body += body ;
301+ } while ( head )
302+ ++ y ;
303+ } while ( ipos )
304+ return [ - 1 , - 1 ] ;
305+ } , npos = ( pos , x , y ) => {
306+ let ipos = ~ pos ;
307+ while ( y -- ) {
308+ ipos &= ipos - 1 ;
309+ }
310+ let body = ipos & - ipos ;
311+ let tail = pos & body - 1 ;
312+ let head = pos ^ tail ;
313+ if ( popcount ( head ) <= x ) {
314+ return 0 ;
315+ }
316+ do {
317+ head &= head - 1 ;
318+ tail |= body ;
319+ body += body ;
320+ } while ( popcount ( head ) != x ) ;
321+ return head | tail ;
322+ } , take = ( x , y ) => {
323+ ++ move ;
324+ turn . textContent = ( ( move & 1 ) ? p1 : p2 ) + '\'s turn' ;
325+ animate ( x , y ) ;
326+ let ipos = ~ currPos ;
327+ while ( y -- ) {
328+ ipos &= ipos - 1 ;
329+ }
330+ let body = ipos & - ipos ;
331+ //console.log(body.toString(2), ipos.toString(2));
332+ let tail = currPos & body - 1 ;
333+ let head = currPos ^ tail ;
334+ if ( popcount ( head ) <= x ) {
335+ return false ;
336+ }
337+ do {
338+ //console.log(x, (head | tail).toString(2));
339+ head &= head - 1 ;
340+ tail |= body ;
341+ body += body ;
342+ } while ( popcount ( head ) != x ) ;
343+ currPos = head | tail ;
344+ if ( currPos == 383 ) {
345+ board . style . filter = '' ;
346+ menu . hidden = false ;
347+ if ( title . textContent . length == 5 ) {
348+ desc . textContent += ' Play again:' ;
349+ }
350+ turn . textContent = 'Game over!' ;
351+ title . textContent = ( ( move & 1 ) ? p2 : p1 ) + ' won!'
352+ move = 1 ;
353+ }
354+ return true ;
355+ } , animate = ( x , y ) => {
356+ propagateRight ( x , y ) ;
357+ while ( y < 6 ) {
358+ const elem = document . getElementById ( 'p' + ( ++ y ) + x ) ;
359+ if ( elem . className == 'piece' )
360+ propagateRight ( x , y ) ;
361+ else
362+ break ;
363+ }
364+ }
365+ </ script >
366+ </ body >
367+ </ html >
0 commit comments