-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.html
658 lines (590 loc) · 20.9 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
<!DOCTYPE html>
<html lang="en">
<head>
<title>Ultimate Duidoku</title>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<link href="https://fonts.googleapis.com/css?family=UnifrakturMaguntia&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Vollkorn+SC&display=swap" rel="stylesheet">
<!-- Stylesheet -->
<style>
p {
font-family: 'Vollkorn SC', cursive;
font-size: 20px;
}
h1 {
font-family: 'Vollkorn SC', cursive;
font-size: 50px;
margin: 10px 0px;
}
h2 {
font-family: 'Vollkorn SC', cursive;
font-size: 35px;
}
button {
font-family: 'UnifrakturMaguntia', cursive;
}
div {
align-content: center;
text-align: center;
min-width: 700px;
}
.button {
background-color: white;
color: black;
padding: 10px 10px;
text-align: center;
box-shadow: 2px 2px;
margin: 3px 1px;
display: inline-block;
font-size: 30px;
width: 60px;
height: 60px;
max-width: 60px;
max-height: 60px;
}
.button:hover {
background-color: lightgrey;
}
.horiz-space {
display: inline-block;
}
.popup {
visibility: hidden;
left: 0px;
top: 0px;
z-index: 1;
display: inline-block;
position: fixed;
background-color: white;
color: black;
padding: 10px 10px;
box-shadow: 2px 2px;
margin: 3px 1px;
font-size: 30px;
width: 60px;
height: 60px;
max-width: 60px;
max-height: 60px;
}
.bottom {
background-color: white;
color: black;
padding: 10px 10px;
text-align: center;
box-shadow: 2px 2px;
margin: 3px 1px;
display: inline-block;
font-size: 30px;
width: 220px;
height: 60px;
max-width: 220px;
max-height: 60px;
font-family: 'Vollkorn SC', cursive;
font-size: 30px;
}
#top-bar {
position: fixed;
align-content: center;
text-align: center;
width: 100%;
top: 0%;
background-color: white;
}
</style>
<!-- Script -->
<script>
/* Element Modification */
// shows the user value entry buttons
var show_popup = function(posx, posy) {
elements = document.getElementsByClassName("popup");
moves = valid_moves(y, x);
console.log(moves);
minx = posx - moves.length * 60 / 2
if (minx < 0) minx = 0;
var j = 0;
for (var i = 0; i < elements.length; i++) {
if (moves.includes(parseInt(elements[i].innerHTML))) {
elements[i].style.visibility = "visible";
elements[i].style.top = posy + "px";
elements[i].style.left = minx + j * 60 + "px";
j++;
}
}
}
// undoes the grid grey out effect
var show_grid = function() {
elements = document.getElementsByClassName("button");
for (var i = 0; i < elements.length; i++) {
elements[i].style.color = "black";
}
document.getElementById("solve-button").style.color = "black";
document.getElementById("autoplay-button").style.color = "black";
}
// greys out the grid and the solve & autoplay buttons
// the user shouldn't try to autoplay or solve while entering
var hide_grid = function() {
elements = document.getElementsByClassName("button");
for (var i = 0; i < elements.length; i++) {
elements[i].style.color = "lightgrey";
}
document.getElementById("solve-button").style.color = "lightgrey";
document.getElementById("autoplay-button").style.color = "lightgrey";
}
// hides the user entry buttons
var hide_popup = function() {
elements = document.getElementsByClassName("popup");
for (var i = 0; i < elements.length; i++) {
elements[i].style.visibility = "hidden";
}
}
// move data from board[][] to the buttons
var copy_board_to_window = function() {
for (var i = 0; i < 9; i++) {
for (var j = 0; j < 9; j++) {
if (board[i][j] === 0) {
document.getElementById(i + "," + j).innerHTML = " ";
} else {
document.getElementById(i + "," + j).innerHTML = board[i][j];
}
}
}
}
// move data from board_copy[][] to the buttons
var copy_board_copy_to_window = function() {
for (var i = 0; i < 9; i++) {
for (var j = 0; j < 9; j++) {
if (board_copy[i][j] === 0) {
document.getElementById(i + "," + j).innerHTML = " ";
} else {
document.getElementById(i + "," + j).innerHTML = board_copy[i][j];
if (board_copy[i][j] == board[i][j]) {
document.getElementById(i + "," + j).style.color = "black";
}
}
}
}
}
/* Board Code */
// alloc a new board
var alloc_board = function() {
var arr = []
for (var i = 0; i < 9; i++) {
var arri = []
for (var j = 0; j < 9; j++) {
arri.push(0)
}
arr.push(arri)
}
return arr;
}
// copy from board into the copy
// useful for solving, as it allows me to keep
// track of the original state
var copy_board = function() {
for (var i = 0; i < 9; i++) {
for (var j = 0; j < 9; j++) {
board_copy[i][j] = board[i][j];
}
}
}
// is the move (row, col, value) valid?
var is_valid_move = function(r, c, v) {
// is value present?
if (board[r][c] != 0) {
return false;
}
// row check
for (var i = 0; i < 9; i++) {
if (board[r][i] == v) return false;
}
// col check
for (var i = 0; i < 9; i++) {
if (board[i][c] == v) return false;
}
// grid check
for (var i = Math.floor(r / 3) * 3; i < Math.floor(r / 3) * 3 + 3; i++) {
for (var j = Math.floor(c / 3) * 3; j < Math.floor(c / 3) * 3 + 3; j++) {
if (board[i][j] == v) return false;
}
}
return true;
}
// list of all valid moves in (row, col)
// used for figuring out which buttons to display
var valid_moves = function(r, c) {
moves = [];
for (var i = 1; i < 10; i++) {
if (is_valid_move(r, c, i)) {
moves.push(i);
}
}
return moves;
}
// is move (row, col, value) valid, but operating on board_copy
var is_valid_copy_move = function(r, c, v) {
if (board_copy[r][c] != 0) {
console.log("filled!");
return false;
}
for (var i = 0; i < 9; i++) {
if (board_copy[r][i] == v) return false;
}
for (var i = 0; i < 9; i++) {
if (board_copy[i][c] == v) return false;
}
for (var i = Math.floor(r / 3) * 3; i < Math.floor(r / 3) * 3 + 3; i++) {
for (var j = Math.floor(c / 3) * 3; j < Math.floor(c / 3) * 3 + 3; j++) {
if (board_copy[i][j] == v) return false;
}
}
return true;
}
// the x index of the next valid cell
var nx = -1;
// the y index of the next valid cell
var ny = -1;
// returns if there is another empty cell to fill
// if so sets, nx and ny
var next_empty = function() {
for (var i = 0; i < 9; i++) {
for (var j = 0; j < 9; j++) {
if (board_copy[i][j] == 0) {
nx = j;
ny = i;
return true;
}
}
}
return false;
}
// check recursivly if the board copy is solvable
var solveable_r = function() {
if (!next_empty()) {
return true;
}
var vx = nx;
var vy = ny;
for (var k = 1; k <= 9; k++) {
if (is_valid_copy_move(vy, vx, k)) {
board_copy[vy][vx] = k;
if (solveable_r())
return true;
board_copy[vy][vx] = 0;
}
}
board[vy][vx] = 0;
return false;
}
// check if the board is solvable
// checks if there are any cells with no moves left first
// as that means the board is unsolvable, and solveable_r()
// can take an abnormally large amount of time on some inputs
var solveable = function() {
for (var i = 0; i < 9; i++) {
for (var j = 0; j < 9; j++) {
if (board[i][j] == 0 && valid_moves(i, j).length == 0)
return false;
}
}
return solveable_r();
}
// the board
var board = alloc_board();
// a copy of the board
// various solve() methods operate on this so as not to disturb
// the master copy of the board
var board_copy = alloc_board();
/* UI Code */
var turn_number = 0;
var entering = false; // is the user entering
var x = -1; // x of the button pressed
var y = -1; // y of the button pressed
// act on the fact that the user pressed the button at (a,b);
var disp = function(a,b) {
if (!entering && board[a][b] == 0) {
var dim = document.getElementById(a + "," + b).getBoundingClientRect();
entering = true;
y = a;
x = b;
show_popup(dim.left, dim.top);
hide_grid();
}
}
// process the fact that the user selected the value v
// to put into (y, x) specified by the button they must
// have clicked on earlier
var process = function(v) {
board[y][x] = v;
turn_number++;
copy_board();
var victor_box = document.getElementById("victor");
if(!next_empty()) {
// tie if no more available moves
victor_box.innerHTML = "The game is a tie!";
} else if (!solveable()) {
// someone might have won
if (turn_number % 2 == 0) {
victor_box.innerHTML = "Player 1 wins";
} else {
victor_box.innerHTML = "Player 2 wins";
}
} else {
// or not
entering = false;
show_grid();
}
// update board on the window
copy_board_to_window();
hide_popup();
}
// is the board currently "solved" due to a call to toggle_solve()?
var solved = false;
// toggle if the current solution calculated by solvable_r()
// should be displayed
var toggle_solve = function() {
// we don't want the user to be able to display the solution
// after having clicked on a field
if (!solved && !entering) {
copy_board();
if(solveable()) {
solved = true;
entering = true;
hide_grid();
copy_board_copy_to_window(); // show the solution in board_copy
// we don't want to grey out the solve button
document.getElementById("solve-button").innerHTML = "Unsolve";
document.getElementById("solve-button").style.color = "black";
}
} else if (solved) {
// "Unsolve" the board
copy_board_to_window();
solved = false;
entering = false;
show_grid();
// update
document.getElementById("solve-button").innerHTML = "Solve";
}
}
// play the next move that the computer would in order to solve the game
var autoplay = function() {
if (!entering) {
entering = true;
copy_board();
if (next_empty()) {
var vx = nx;
var vy = ny;
if(solveable()) {
board[vy][vx] = board_copy[vy][vx];
copy_board_to_window();
}
copy_board();
// someone might have tied by calling autoplay with 1 valid move left
// as autoplay can only ever be called on solvable boards (1)
// we only need to check for ties
// (1) We know this because:
// The starting board is solvable
// autoplay() does the next valid move, leaving the board solvable
// if a user entered a move which leads to an unsolvable game,
// that will be caught in process() by solvable()
if (!next_empty()) {
document.getElementById("victor").innerHTML = "The game is a tie!";
hide_grid();
entering = true;
} else {
entering = false;
}
} else {
entering = false;
}
}
}
// oh hey, it's the init message
var init_message = function() {
console.log("Hey there! Happy to see you!");
console.log("Feel free to poke around! Remember that you can also find the source at https://github.com/khemritolya/ultimate-duidoku");
console.log("If you've got any questions or concerns, feel free to drop me a line!");
console.log("Email: lrh74[at]cornell[dot]edu");
}
window.onload = init_message;
</script>
</head>
<body>
<!-- Spacer to to fix title bar covering rules -->
<br><br><br><br><br>
<!-- Rules area -->
<div>
<p>A two player game where the players insert numbers into squares in turn</p>
<h2>Rules</h2>
<p>1. Moves are valid as in Sudoku<br>2. Moves alternate from Player 1 to Player 2<br>3. If your move causes an unsolvable board, you lose,<br>the other player wins<br>4. If the board is solved, it is a tie</p>
<br>
<hr>
<br><br>
</div>
<!-- Generated using a python program. I would not want to write this by hand. -->
<div>
<button class="button" id="0,0" onclick="disp(0,0)"> </button>
<button class="button" id="0,1" onclick="disp(0,1)"> </button>
<button class="button" id="0,2" onclick="disp(0,2)"> </button>
<p class = "horiz-space"> </p>
<button class="button" id="0,3" onclick="disp(0,3)"> </button>
<button class="button" id="0,4" onclick="disp(0,4)"> </button>
<button class="button" id="0,5" onclick="disp(0,5)"> </button>
<p class = "horiz-space"> </p>
<button class="button" id="0,6" onclick="disp(0,6)"> </button>
<button class="button" id="0,7" onclick="disp(0,7)"> </button>
<button class="button" id="0,8" onclick="disp(0,8)"> </button>
<p class = "horiz-space"> </p>
<br>
<button class="button" id="1,0" onclick="disp(1,0)"> </button>
<button class="button" id="1,1" onclick="disp(1,1)"> </button>
<button class="button" id="1,2" onclick="disp(1,2)"> </button>
<p class = "horiz-space"> </p>
<button class="button" id="1,3" onclick="disp(1,3)"> </button>
<button class="button" id="1,4" onclick="disp(1,4)"> </button>
<button class="button" id="1,5" onclick="disp(1,5)"> </button>
<p class = "horiz-space"> </p>
<button class="button" id="1,6" onclick="disp(1,6)"> </button>
<button class="button" id="1,7" onclick="disp(1,7)"> </button>
<button class="button" id="1,8" onclick="disp(1,8)"> </button>
<p class = "horiz-space"> </p>
<br>
<button class="button" id="2,0" onclick="disp(2,0)"> </button>
<button class="button" id="2,1" onclick="disp(2,1)"> </button>
<button class="button" id="2,2" onclick="disp(2,2)"> </button>
<p class = "horiz-space"> </p>
<button class="button" id="2,3" onclick="disp(2,3)"> </button>
<button class="button" id="2,4" onclick="disp(2,4)"> </button>
<button class="button" id="2,5" onclick="disp(2,5)"> </button>
<p class = "horiz-space"> </p>
<button class="button" id="2,6" onclick="disp(2,6)"> </button>
<button class="button" id="2,7" onclick="disp(2,7)"> </button>
<button class="button" id="2,8" onclick="disp(2,8)"> </button>
<p class = "horiz-space"> </p>
<br>
</div>
<div>
<br>
<button class="button" id="3,0" onclick="disp(3,0)"> </button>
<button class="button" id="3,1" onclick="disp(3,1)"> </button>
<button class="button" id="3,2" onclick="disp(3,2)"> </button>
<p class = "horiz-space"> </p>
<button class="button" id="3,3" onclick="disp(3,3)"> </button>
<button class="button" id="3,4" onclick="disp(3,4)"> </button>
<button class="button" id="3,5" onclick="disp(3,5)"> </button>
<p class = "horiz-space"> </p>
<button class="button" id="3,6" onclick="disp(3,6)"> </button>
<button class="button" id="3,7" onclick="disp(3,7)"> </button>
<button class="button" id="3,8" onclick="disp(3,8)"> </button>
<p class = "horiz-space"> </p>
<br>
<button class="button" id="4,0" onclick="disp(4,0)"> </button>
<button class="button" id="4,1" onclick="disp(4,1)"> </button>
<button class="button" id="4,2" onclick="disp(4,2)"> </button>
<p class = "horiz-space"> </p>
<button class="button" id="4,3" onclick="disp(4,3)"> </button>
<button class="button" id="4,4" onclick="disp(4,4)"> </button>
<button class="button" id="4,5" onclick="disp(4,5)"> </button>
<p class = "horiz-space"> </p>
<button class="button" id="4,6" onclick="disp(4,6)"> </button>
<button class="button" id="4,7" onclick="disp(4,7)"> </button>
<button class="button" id="4,8" onclick="disp(4,8)"> </button>
<p class = "horiz-space"> </p>
<br>
<button class="button" id="5,0" onclick="disp(5,0)"> </button>
<button class="button" id="5,1" onclick="disp(5,1)"> </button>
<button class="button" id="5,2" onclick="disp(5,2)"> </button>
<p class = "horiz-space"> </p>
<button class="button" id="5,3" onclick="disp(5,3)"> </button>
<button class="button" id="5,4" onclick="disp(5,4)"> </button>
<button class="button" id="5,5" onclick="disp(5,5)"> </button>
<p class = "horiz-space"> </p>
<button class="button" id="5,6" onclick="disp(5,6)"> </button>
<button class="button" id="5,7" onclick="disp(5,7)"> </button>
<button class="button" id="5,8" onclick="disp(5,8)"> </button>
<p class = "horiz-space"> </p>
<br>
</div>
<div>
<br>
<button class="button" id="6,0" onclick="disp(6,0)"> </button>
<button class="button" id="6,1" onclick="disp(6,1)"> </button>
<button class="button" id="6,2" onclick="disp(6,2)"> </button>
<p class = "horiz-space"> </p>
<button class="button" id="6,3" onclick="disp(6,3)"> </button>
<button class="button" id="6,4" onclick="disp(6,4)"> </button>
<button class="button" id="6,5" onclick="disp(6,5)"> </button>
<p class = "horiz-space"> </p>
<button class="button" id="6,6" onclick="disp(6,6)"> </button>
<button class="button" id="6,7" onclick="disp(6,7)"> </button>
<button class="button" id="6,8" onclick="disp(6,8)"> </button>
<p class = "horiz-space"> </p>
<br>
<button class="button" id="7,0" onclick="disp(7,0)"> </button>
<button class="button" id="7,1" onclick="disp(7,1)"> </button>
<button class="button" id="7,2" onclick="disp(7,2)"> </button>
<p class = "horiz-space"> </p>
<button class="button" id="7,3" onclick="disp(7,3)"> </button>
<button class="button" id="7,4" onclick="disp(7,4)"> </button>
<button class="button" id="7,5" onclick="disp(7,5)"> </button>
<p class = "horiz-space"> </p>
<button class="button" id="7,6" onclick="disp(7,6)"> </button>
<button class="button" id="7,7" onclick="disp(7,7)"> </button>
<button class="button" id="7,8" onclick="disp(7,8)"> </button>
<p class = "horiz-space"> </p>
<br>
<button class="button" id="8,0" onclick="disp(8,0)"> </button>
<button class="button" id="8,1" onclick="disp(8,1)"> </button>
<button class="button" id="8,2" onclick="disp(8,2)"> </button>
<p class = "horiz-space"> </p>
<button class="button" id="8,3" onclick="disp(8,3)"> </button>
<button class="button" id="8,4" onclick="disp(8,4)"> </button>
<button class="button" id="8,5" onclick="disp(8,5)"> </button>
<p class = "horiz-space"> </p>
<button class="button" id="8,6" onclick="disp(8,6)"> </button>
<button class="button" id="8,7" onclick="disp(8,7)"> </button>
<button class="button" id="8,8" onclick="disp(8,8)"> </button>
<p class = "horiz-space"> </p>
</div>
<!-- Victory area -->
<div>
<h2 id="victor"></h2>
</div>
<!-- Helper Buttons -->
<div>
<button class="bottom" onclick="window.location.reload()">Restart</button>
<button class="bottom" id="solve-button" onclick="toggle_solve()">Solve</button>
<button class="bottom" id="autoplay-button" onclick="autoplay()">Play for Me</button>
<br><br><br>
<hr>
</div>
<!-- Additional Information Section -->
<div>
<br><br>
<p><b>Additional Information:</b></p>
<p>Clicking "Solve" will display the current board's solution,<br>
but will not end the game! You can "Unsolve" it</p>
<p>"Play for Me" plays the next valid move<br>which leads to a solvable board</p>
<br>
<p>From: <a href="https://www.amazon.com/Economists-Miscellany-Kaushik-Basu/dp/0198072503">An Economist's Miscellany</a>
<p>Concept: <a href="http://kaushikbasu.org/">Kaushik Basu</a><p>
<p> Author: <a href="https://github.com/khemritolya">Luis Hoderlein</a></p>
</div>
<br>
<!-- The buttons that the user uses to enter input -->
<div>
<button class="popup" id="1" onclick="process(1)">1</button>
<button class="popup" id="2" onclick="process(2)">2</button>
<button class="popup" id="3" onclick="process(3)">3</button>
<button class="popup" id="4" onclick="process(4)">4</button>
<button class="popup" id="5" onclick="process(5)">5</button>
<button class="popup" id="6" onclick="process(6)">6</button>
<button class="popup" id="7" onclick="process(7)">7</button>
<button class="popup" id="8" onclick="process(8)">8</button>
<button class="popup" id="9" onclick="process(9)">9</button>
</div>
<div id="top-bar">
<h1>Ultimate Duidoku</h1>
<hr>
</div>
</body>
</html>