Skip to content

Commit 1a41921

Browse files
committed
Sync changes from UMich Webspace
1 parent 18afc89 commit 1a41921

5 files changed

Lines changed: 848 additions & 309 deletions

File tree

Resume.pdf

19.4 KB
Binary file not shown.

chomp.html

Lines changed: 367 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,367 @@
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>

chomp.ico

190 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)