├── .gitignore ├── README.md ├── index.pug ├── animated-dice ├── index.pug ├── styles.scss └── index.html └── styles.scss /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Snakes and Ladders 2 | 3 | Snakes and Ladders is an ancient Indian board game regarded today as a worldwide classic. A number of 'ladders' and 'snakes' are pictured on the board, each connecting two specific board squares. The object of the game is to navigate one's game piece, according to die rolls, from the start (bottom square) to the finish (top square), helped or hindered by ladders and snakes respectively.[1](https://en.wikipedia.org/wiki/Snakes_and_Ladders) 4 | 5 | This is a multiplayer version (1-4 players) developed using HTML and CSS **without any JavaScript**. It is based on the idea that many classic board games are actually Finite-State Machines (FSM), so they can be recreated simulating the states of the automaton with radio buttons, labels, and some CSS styling... without any other scripting/imperative language. 6 | 7 | ## How it works 8 | 9 | ### Simulation of states 10 | 11 | In theory, the number of states/radio buttons for this game could be up to 1004 = 100,000,000, just to represent the different positions of the 4 game pieces on the board. And we would need to multiply that number by 4 so we could also track the player's turn. Making it a 400,000,000-state automaton. 12 | 13 | That would be an insane number of radio buttons to maintain and render by the browser, so we only keep track of 100 positions for each player (400 radio buttons) and an additional set of 4 radio buttons to keep track of the player's turn. This reduction helps ease the rendering of the page, but it brings an usability problem: we need the "Next Player" button to jump from one player to the next one. 14 | 15 | Each of the 400 position radio buttons will have label associated to it that will be used as part of the dice (see below) to move from one state/position into another. 16 | 17 | ### Game Logic 18 | 19 | There are many parts moving, but the logic in itself is simple: the labels for a player's next six available positions are shown (in the form of a dice), the user clicks on one of them activating the radio button associated to it, and that triggers a series of events: the player's piece moves on the board, a sound plays, the next six available position `label`s are shown... 20 | 21 | The trick for doing this is using the general sibling selector (`~`) and the `:checked` pseudo class. The radio buttons will be at a global level, and the board, pieces, sounds, etc. will be in different containers. To activate the action of a radio button, we will need to verify that it has been activated (with `:checked`), and then proceed to select the element that will go through a change. 22 | 23 | For example, when the radio button por position 18 is activated, we can select the player's piece by doing something like this in CSS (just for demo, not the actual code from the game): 24 | 25 | #radio-18:checked ~ #board #player1-piece { 26 | ... 27 | } 28 | 29 | And inside that CSS rule, we would add the coordinates of the 18th tile using `top` and `left`. Easily animating the move by having `transition` set up. 30 | 31 | ### Dice 32 | 33 | The dice is an interesting part of the game: how to develop a random number generator using HTML and CSS without JavaScript? The answer is you can't. There is no real way to do it, because both HTML and CSS are languages that describe the content of the page, but none of them have the capability to generate random content. So a random dice is out of the question. 34 | 35 | ...but you can develop a pseudo-random one! 36 | 37 | By having a set of stacked `label` elements that change their position on top every few milliseconds, you can create a random effect... it is not really random, but it looks like it, and that's more than enough in this case, as humans would need really good time-control in order to take advantage of that pseudo-randomness. 38 | 39 | This was a bit more challenging to develop than expected (in fact, it required some [help from our friends at StackOverflow](https://stackoverflow.com/q/51449737/3695983)) because the click effect (on the `label`) is only effective if the active element when the mouse button goes down is the same as when the mouse button goes up. And as the `label`s are part of an animation, sometimes the mouse button up moment happened after the `label` was no longer the active one. A workaround for this (as explained on the linked question) is to use CSS pseudo-elements and different positioning. 40 | 41 | ### Sound 42 | 43 | > Notice: The sound **only works on Chrome**. On other browsers, the sounds may play once when the page is loaded (and then never play again), or not play at all. 44 | 45 | Instead of using an `audio` tag for the sound, we used `embed`. The `embed` element can be used to embed/import external content to our page (in this case, an MP3 file). This `embed` element is contained in a hidden section that is only made visible for a fraction of a second when an action that triggers the sound happens. 46 | 47 |
48 | 49 |
50 | 51 | According to the [definition of the `embed` element in the living HTML standard](https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-embed-element): "_Whenever an `embed` element that was not potentially active becomes potentially active [...] the user agent must queue a task using the embed task source to run the embed element setup steps for that element_". So by making the `embed` container visible for a little time, we are actually triggering the rendering of the `embed` element, and the sound should play. 52 | 53 | So, at least in theory, Chrome's behavior should actually be the expected one in this case. 54 | 55 | ## Potential improvements 56 | 57 | Apart from visual changes to make it look nicer, there are other things that could be added/changed to improve the game usability without impacting the general dynamics of the game (and still without using any JavaScript): 58 | 59 | - Have the max number of players as a variable (in Pug and SCSS). Right now 4 is used as a "magic number" to generate the radio buttons and elements on the page, if it was saved as a variable, the game could be done even more generic and add more players easily (just by updating the value of the variable). 60 | - Have more combinations of colors. Right now there are only 4 possibilities to pick from, but potentially there could be all of them, either by directly listing them or by changing the way it is designed so each user picks the color they want. 61 | - Add international versions. This would be a simple change that would put the instructions and text copies in different languages. One radio button by language would be required, and then the text could be specified by hiding/showing repeated elements, or by having the texts in pseudo-elements. 62 | 63 | ## Other versions 64 | 65 | Current and older versions playable on CodePen: 66 | 67 | - [Current 1-4 player version](https://codepen.io/alvaromontoro/details/gjWPNW/) 68 | - [Current 1-4 player version (with animated dice)](https://codepen.io/alvaromontoro/pen/OwwQMm/) 69 | - [Simplified two-player version](https://codepen.io/alvaromontoro/details/zLNdZQ/) 70 | - [One-player version](https://codepen.io/alvaromontoro/pen/ejzJBJ/) 71 | 72 | ## Bibliography and Reference 73 | 74 | - [Wikipedia's article on Snakes and Ladders](https://en.wikipedia.org/wiki/Snakes_and_Ladders) 75 | - [Wikipedia's article on Finite State Machines](https://en.wikipedia.org/wiki/Finite-state_machine) 76 | - [HTML Living Standard: The `embed` element](https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-embed-element) 77 | - [MDN's article on `label`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label) 78 | - [MDN's article on CSS Selectors](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors) 79 | - [MDN's article on General Sibling Selectors (`~`)](https://developer.mozilla.org/en-US/docs/Web/CSS/General_sibling_selectors) 80 | -------------------------------------------------------------------------------- /index.pug: -------------------------------------------------------------------------------- 1 | // checkboxes to control states and variables 2 | - var n = 1; 3 | while n < 5 4 | input(type="radio", name="players", id="players"+n, checked=(n == 1), value=(n++)) 5 | - var n = 1; 6 | while n < 5 7 | input(type="radio", name="turn", id="turn"+n, checked=(n==1), value=(n++)) 8 | - var n = 1; 9 | while n < 5 10 | input(type="radio", name="colors", checked=(n == 1), id="colors"+(n++)) 11 | input(type="checkbox", id="game-time", class="game-time", name="game-time") 12 | input(type="checkbox", name="show-info", id="show-info") 13 | input(type="checkbox", name="play-fx", id="play-fx") 14 | - var n = 1; 15 | while n < 6 16 | input(type="radio", id="show-instructions-"+(n++), class="game-time", name="show-instructions") 17 | 18 | // inputs and labels that control the piece position for player 1 19 | each players in [1,2,3,4] 20 | - var n = 0; 21 | while n < 107 22 | input(type="radio", name="cb-player"+players, id="cb-pl"+players+"-"+n, class="cb", checked=(n==0)) 23 | label.dice(for="cb-pl"+players+"-"+(n++)) 24 | 25 | // game settings menu (# of players, colors) 26 | #options.scrim 27 | .box 28 | h1 Game Settings 29 | div 30 | h2 Number of players 31 | div.flex.option-players 32 | - var n = 1; 33 | while n < 5 34 | label(for="players"+n) #{n++} 35 | h2 Piece colors 36 | div.option-colors 37 | - var n = 1; 38 | while n < 5 39 | label.flex(for="colors"+(n++)) 40 | span 41 | span 42 | span 43 | span 44 | span 45 | h2 Sound options 46 | label#activate-fx(for="play-fx") FX Sound 47 | div 48 | label.option-start(for="show-instructions-1") START GAME 49 | 50 | // pop-up with information about the game and the author 51 | #info.scrim 52 | .box 53 | h1 About 54 | div 55 | p Snakes and Ladders is an ancient Indian board game regarded today as a worldwide classic. A number of 'ladders' and 'snakes' are pictured on the board, each connecting two specific board squares. The object of the game is to navigate one's game piece, according to die rolls, from the start (bottom square) to the finish (top square), helped or hindered by ladders and snakes respectively. 56 | sup 57 | a(href="https://en.wikipedia.org/wiki/Snakes_and_Ladders", target="_blank") [1] 58 | p This version was developed by 59 | a(href="https://github.com/alvaromontoro", target="_blank") Alvaro Montoro 60 | | using HTML and CSS, to practice Pug and Sass, and without any JavaScript line of code. 61 | p The idea behind this project is that many classic board games are actually Finite-State Machines (FSM), so they can be recreated simulating the states of the automaton with radio buttons, labels, and some CSS styling. 62 | p 63 | label#close-button(for="show-info") Close 64 | 65 | // animated instructions section 66 | #instructions 67 | label#go-to-game(for="game-time") SKIP INSTRUCTIONS › 68 | #step 69 | div 70 | label(for="show-instructions-2") 71 | label(for="show-instructions-3") 72 | label(for="show-instructions-4") 73 | label(for="show-instructions-5") 74 | label(for="game-time") PLAY › 75 | 76 | // the game section: board, dice, snakes, ladders, and all that jazz... 77 | #game 78 | #player-info.tab 79 | span Player 80 | //label#dice.tab 81 | div#action.tab 82 | label#turn-changer1(for="turn1") 83 | label#turn-changer2(for="turn2") 84 | label#turn-changer3(for="turn3") 85 | label#turn-changer4(for="turn4") 86 | div#fx-click-action.fx-sound 87 | embed(src="https://studiokah.com/tests/click2.mp3") 88 | label#more-info.tab(for="show-info") 89 | div#fx-click-more-info.fx-sound 90 | embed(src="https://studiokah.com/tests/click2.mp3") 91 | // Board based on the one from codewars: https://www.codewars.com/kata/snakes-and-ladders-1 92 | #board 93 | each val in [100,99,98,97,96,95,94,93,92,91,81,82,83,84,85,86,87,88,89,90,80,79,78,77,76,75,74,73,72,71,61,62,63,64,65,66,67,68,69,70,60,59,58,57,56,55,54,53,52,51,41,42,43,44,45,46,47,48,49,50,40,39,38,37,36,35,34,33,32,31,21,22,23,24,25,26,27,28,29,30,20,19,18,17,16,15,14,13,12,11,1,2,3,4,5,6,7,8,9,10] 94 | div(class="tile tile"+val, data-index=val) 95 | svg#snakeladders(viewBox="0 0 600 600", preserveAspectRatio="none") 96 | g.ladders-big 97 | path(d="M89,574 140,392 M102,578 153,396 M388,581 385,508 M402,580 399,507 M449,552 554,395 M460,560 565,403 M320,516 333,444 M334,518 347,446 M24,436 74,329 M36,442 87,334 M430,458 210,99 M441,450 222,93 M264,393 214,339 M273,384 224,330 M563,284 391,203 M568,271 396,190 M570,149 559,31 M584,148 573,30 M142,158 155,40 M156,160 169,42 M397,101 379,30 M411,97 392,26") 98 | g.ladders-small 99 | path(d="M91,568 104,573 M94,556 105,561 M97,544 108,549 M100,532 111,537 M103,520 114,525 M107,508 118,513 M111,496 122,501 M114,484 125,489 M117,472 128,477 M121,460 132,465 M124,448 135,453 M128,436 139,441 M131,424 142,429 M134,412 145,417 M138,400 149,405 M388,575 401,574 M388,563 401,562 M388,552 401,551 M388,541 401,540 M388,530 400,529 M388,518 400,517 M452,546 464,555 M459,536 470,544 M466,526 476,533 M473,516 482,522 M479,508 490,515 M485,498 496,506 M492,488 502,495 M498,479 509,486 M504,469 515,477 M512,459 522,467 M517,449 528,457 M523,440 534,448 M530,430 541,438 M536,421 548,429 M542,412 554,419 M549,402 560,409 M322,510 335,512 M324,498 337,500 M326,486 339,488 M328,475 341,477 M330,463 343,465 M332,452 345,454 M258,388 268,378 M250,379 260,369 M243,370 252,361 M235,362 245,352 M227,353 236,344 M219,345 229,335 M27,430 40,436 M32,419 45,425 M37,408 49,414 M42,398 54,404 M47,388 59,394 M51,377 64,383 M56,366 69,372 M61,356 75,362 M66,345 79,351 M71,335 84,341 M427,453 438,444 M420,443 432,434 M414,433 426,424 M408,423 420,414 M402,413 413,404 M395,402 407,393 M388,391 400,382 M381,379 393,370 M374,368 386,359 M367,356 378,347 M359,344 371,336 M352,332 364,324 M345,320 357,312 M338,308 350,300 M330,296 342,288 M323,284 335,276 M316,272 328,264 M308,260 320,252 M301,248 313,240 M294,236 305,228 M287,224 298,216 M280,212 291,205 M273,200 284,193 M267,188 277,181 M259,175 269,168 M251,163 261,156 M244,151 254,144 M236,139 246,132 M229,127 239,120 M222,115 232,108 M215,103 225,96 M143,152 157,154 M144,140 158,142 M145,128 159,130 M146,116 160,118 M147,104 161,106 M149,92 163,94 M150,80 164,82 M151,68 165,70 M153,56 167,58 M154,44 168,46 M396,95 409,91 M393,84 406,80 M390,73 403,69 M387,62 400,58 M384,51 397,47 M381,40 394,36 M570,143 583,142 M568,130 582,129 M566,117 580,116 M565,104 577,103 M564,91 576,90 M563,78 575,77 M562,65 574,64 M561,52 573,51 M560,38 572,37 M556,281 562,268 M546,276 552,263 M536,271 542,258 M526,266 532,254 M516,261 522,249 M506,255 512,244 M496,250 502,239 M486,245 492,235 M476,240 482,230 M466,236 472,225 M456,231 462,220 M446,226 451,216 M436,221 441,211 M425,218 431,206 M415,213 421,201 M405,208 411,196 M395,204 401,192") 100 | path(d="M20,42 28,42 28,35 32,35 32,42 40,42 40,32 45,32 30,20 15,32 20,32 Z M20,578 30,578 30,583 40,570 30,557 30,562 20, 562Z", fill="rgba(0,0,0,0.25)") 101 | path.snake(d="M 90,226 C 98,257 69,322 128,332 187,342 181,381 120,382 59,383 53,425 54,433 56,450 87,467 87,481 87,495 76,506 76,512 84,505 95,496 96,480 97,464 60,447 64,425 68,403 99,392 134,391 169,390 219,344 131,322 94,313 100,279 101,256 102,233 101,229 100,225 105,223.62 106,214 96,210 86,205 65.75,211 83,223 85.88,225 88,226 90,226 Z", fill="#73880A") 102 | circle.eye(cx=97, cy=212, r=5) 103 | path.snake(d="M 22,282 C 35,271 58,266 81,273 104,280 144,278 145,253 146,228 175,208 193,213 204,198 220,210 220,216 220,222 201,229 193,221 182,222 157,229 156,252 155,275 139,297 86,281 33,268 22,283 22,283 Z", fill="#D15600") 104 | circle.eye(cx=203, cy=210, r=5) 105 | path.snake(d="M 453,271 C 433,285 396,289 400,261 405,233 394,223 369,223 343,224 319,167 376,161 379,168 401,168 402,156 403,143 376,143 375,152 300,162 333,233 366,233 402,233 388,258 389,266 390,281 414,309 453,272 Z", fill="#6BBA70") 106 | circle.eye(cx=387, cy=148, r=5) 107 | path.snake(d="M 457,199 C 429,161 465,134 489,158 512,182 542,159 540,129 538,99 523,98 522,98 528,91 505,75 495,97 503,113 518,105 519,105 519,105 530,110 531,130 532,150 513,169 492,149 471,129 445,143 443,159 442,174 449,194 457,200 Z", fill="#C79810") 108 | circle.eye(cx=512, cy=90, r=5) 109 | path.snake(d="M 441,95 C 457,93 479,72 445,52 415,33 434,16 451,16 468,16 489,39 492,40 495,28 521,33 521,43 521,54 496,54 490,48 490,48 466,23 450,25 434,27 439,43 459,51 479,61 473,101 441,96 Z", fill="#356AA0") 110 | circle.eye(cx=504, cy=35, r=5) 111 | path.snake(d="M 278,457 C 291,438 262,428 247,444 230,464 193,450 231,420 256,400 300,425 335,395 349,381 334,338 334,338 334,338 348,325 325,324 301,322 306,341 326,340 334,358 346,401 285,401 231,401 205,416 205,446 206,472 244,462 250,452 258,441 281,435 278,458 Z", fill="#3F4C6B") 112 | circle.eye(cx=327, cy=325, r=5) 113 | path.snake(d="M 343,584 C 352,567 344,553 320,553 296,553 286,562 279,568 272,573 238,592 238,558 237,524 253,517 253,517 253,517 270,517 270,509 270,498 238,501 247,514 227,537 230,556 230,561 229,567 239,605 281,575 324,544 349,562 343,585 Z", fill="#008C00") 114 | circle.eye(cx=257, cy=503, r=5) 115 | path.snake(d="M 572,506 C 555,514 511,516 518,456 526,396 518,380 490,388 457,397 436,346 492,335 507,341 518,334 518,326 518,317 494,313 489,326 422,337 448,410 489,398 525,384 510,449 510,456 510,462 502,547 572,508 Z", fill="#D01F3C") 116 | circle.eye(cx=503, cy=320, r=5) 117 | path.snake(d="M 19,138 C 45,154 84,152 63,119 43,87 59,85 79,84 99,83 103,77 94,40 85,46 73,39 72,32 72,25 109,13 102,35 117,69 108,100 79,94 52,88 70,114 73,118 82,127 83,190 18,138 Z", fill="#2096cE") 118 | circle.eye(cx=89, cy=27, r=5) 119 | path.snake(d="M 322,152 C 302,168 282,153 285,137 287,120 287,121 275,110 263,99 295,79 291,62 287,45 297,30 318,38 324,51 346,48 346,39 347,30 327,20 320,31 295,18 274,33 280,57 286,82 252,86 262,111 265,118 280,122 275,137 271,148 291,185 322,154 Z", fill="#CC4444") 120 | circle.eye(cx=330, cy=30, r=5) 121 | each player in [1,2,3,4] 122 | svg.piece(id="piece-player-"+player, viewBox="0 0 100 100") 123 | g 124 | path(d="M 50,1 C 50,1 80,85 80,85 70,96 60,99 50,99 40,99 30,96 20,85 20,85 50,1 50,1 Z") 125 | circle(cx=50, cy=21, r=20) 126 | rect(x=44, y=25, width=12, height=22, stroke="none") 127 | 128 | #snake-popup.scrim 129 | div.box 130 | h1 Snake!!! 131 | div Oh no! The snake bit you! You'll have to move your piece to the snake's tail. 132 | div 133 | each players in [1,2,3,4] 134 | each val, index in {16:6, 46:25, 49:11, 62:19, 64:60, 74:53, 89:68, 92:88, 95:75, 99:80} 135 | label(id="snake-pl"+players+"-"+index, for="cb-pl"+players+"-"+val) Move Down 136 | svg(viewBox="0 0 100 100") 137 | path(d="M1,32 4,32 M6,34 4,32 M4,32 10,20", stroke="black", stroke-width="1") 138 | path(d="M 30,70 C 49,56 60,84 33,92 -17,103 0,50 24,45 32,44 37,41 29,27 26,33 5,35 3,24 4,13 26,3 37,16 49,30 49,52 28,55 8,59 0,94 33,82 56,72 35,66 30,71 Z", fill="#73B80A", stroke-width="0") 139 | circle(cx=18, cy=15, r=5, fill="black", stroke="white", stroke-width=6) 140 | 141 | 142 | #ladder-popup.scrim 143 | div.box 144 | h1 Ladder! 145 | div Yay! You found a ladder! You can climb up now. 146 | div 147 | each players in [1,2,3,4] 148 | each val, index in {2:38, 7:14, 8:31, 15:26, 21:42, 28:84, 36:44, 51:67, 71:91, 78:98, 87:94} 149 | label(id="ladder-pl"+players+"-"+index, for="cb-pl"+players+"-"+val) Move Up 150 | svg(viewBox="0 0 100 100") 151 | path(d="M5,85 20,10", stroke="#8B4513", stroke-width="10", "stroke-linecap"="round") 152 | path(d="M10,67 30,71 M15,45 35,49 M20,23 40,27", stroke="#aB6533", stroke-width="5", "stroke-linecap"="round") 153 | path(d="M30,90 45,15", stroke="#8B4513", stroke-width="10", "stroke-linecap"="round") 154 | 155 | #home-popup.scrim 156 | div.box 157 | h1 Home 158 | div You made it home, but you didn't do it in an exact jump so you will bounce back the remaining tiles. 159 | div 160 | each players in [1,2,3,4] 161 | each val, index in {101:99, 102:98, 103:97, 104:96, 105:95, 106:94} 162 | label(id="home-pl"+players+"-"+index, for="cb-pl"+players+"-"+val) Bounce back 163 | svg(viewBox="0 0 100 100") 164 | path(d="M12,85 12,40 5,40 30,5 55,40 48,40 48,85 35,85 35,60 25,60 25,85 Z", fill="#8Ba5f3") 165 | path(d="M30,65 C 30,60 25,55 20,55 15,55 10,60 10,65 10,70 15,75 20,75 20,75 20,75 50,75 M40,65 50,75 40,85", stroke="#eeee60", stroke-width=6, fill="none") 166 | 167 | #congratulations.scrim 168 | div.box 169 | h1 Congratulations! 170 | div You made it home safely! You won! 171 | div Reload the page to play again. 172 | svg(viewBox="0 0 100 100") 173 | path(d="M5,10 C 5,10 65,10 65,10 65,10 65,40 65,40 65,50 55,70 35,70 15,70 5,50 5,40 Z", fill="#fede60") 174 | path(d="M35,40 35,85 M20,85 50,85", stroke="#fede60", stroke-width=12, fill="none", stroke-linecap="round") 175 | 176 | div#fx-dice.fx-sound 177 | embed(src="https://studiokah.com/tests/dice.mp3") 178 | div#fx-click.fx-sound 179 | // Click sound by Sebastian. Source: http://soundbible.com/1705-Click2.html 180 | embed(src="https://studiokah.com/tests/click2.mp3") -------------------------------------------------------------------------------- /animated-dice/index.pug: -------------------------------------------------------------------------------- 1 | // checkboxes to control states and variables 2 | - var n = 1; 3 | while n < 5 4 | input(type="radio", name="players", id="players"+n, checked=(n == 1), value=(n++)) 5 | - var n = 1; 6 | while n < 5 7 | input(type="radio", name="turn", id="turn"+n, checked=(n==1), value=(n++)) 8 | - var n = 1; 9 | while n < 5 10 | input(type="radio", name="colors", checked=(n == 1), id="colors"+(n++)) 11 | input(type="checkbox", id="game-time", class="game-time", name="game-time") 12 | input(type="checkbox", name="show-info", id="show-info") 13 | input(type="checkbox", name="play-fx", id="play-fx") 14 | - var n = 1; 15 | while n < 6 16 | input(type="radio", id="show-instructions-"+(n++), class="game-time", name="show-instructions") 17 | 18 | // inputs and labels that control the piece position for player 1 19 | each players in [1,2,3,4] 20 | - var n = 0; 21 | while n < 107 22 | input(type="radio", name="cb-player"+players, id="cb-pl"+players+"-"+n, class="cb", checked=(n==0)) 23 | label.dice(for="cb-pl"+players+"-"+(n++)) 24 | 25 | // game settings menu (# of players, colors) 26 | #options.scrim 27 | .box 28 | h1 Game Settings 29 | div 30 | h2 Number of players 31 | div.flex.option-players 32 | - var n = 1; 33 | while n < 5 34 | label(for="players"+n) #{n++} 35 | h2 Piece colors 36 | div.option-colors 37 | - var n = 1; 38 | while n < 5 39 | label.flex(for="colors"+(n++)) 40 | span 41 | span 42 | span 43 | span 44 | span 45 | h2 Sound options 46 | label#activate-fx(for="play-fx") FX Sound 47 | div 48 | label.option-start(for="show-instructions-1") START GAME 49 | 50 | // pop-up with information about the game and the author 51 | #info.scrim 52 | .box 53 | h1 About 54 | div 55 | p Snakes and Ladders is an ancient Indian board game regarded today as a worldwide classic. A number of 'ladders' and 'snakes' are pictured on the board, each connecting two specific board squares. The object of the game is to navigate one's game piece, according to die rolls, from the start (bottom square) to the finish (top square), helped or hindered by ladders and snakes respectively. 56 | sup 57 | a(href="https://en.wikipedia.org/wiki/Snakes_and_Ladders", target="_blank") [1] 58 | p This version was developed by 59 | a(href="https://github.com/alvaromontoro", target="_blank") Alvaro Montoro 60 | | using HTML and CSS, to practice Pug and Sass, and without any JavaScript line of code. 61 | p The idea behind this project is that many classic board games are actually Finite-State Machines (FSM), so they can be recreated simulating the states of the automaton with radio buttons, labels, and some CSS styling. 62 | p 63 | label#close-button(for="show-info") Close 64 | 65 | // animated instructions section 66 | #instructions 67 | label#go-to-game(for="game-time") SKIP INSTRUCTIONS › 68 | #step 69 | div 70 | label(for="show-instructions-2") 71 | label(for="show-instructions-3") 72 | label(for="show-instructions-4") 73 | label(for="show-instructions-5") 74 | label(for="game-time") PLAY › 75 | 76 | // the game section: board, dice, snakes, ladders, and all that jazz... 77 | #game 78 | #player-info.tab 79 | span Player 80 | //label#dice.tab 81 | div#action.tab 82 | label#turn-changer1(for="turn1") 83 | label#turn-changer2(for="turn2") 84 | label#turn-changer3(for="turn3") 85 | label#turn-changer4(for="turn4") 86 | div#fx-click-action.fx-sound 87 | embed(src="https://studiokah.com/tests/click2.mp3") 88 | label#more-info.tab(for="show-info") 89 | div#fx-click-more-info.fx-sound 90 | embed(src="https://studiokah.com/tests/click2.mp3") 91 | // Board based on the one from codewars: https://www.codewars.com/kata/snakes-and-ladders-1 92 | #board 93 | each val in [100,99,98,97,96,95,94,93,92,91,81,82,83,84,85,86,87,88,89,90,80,79,78,77,76,75,74,73,72,71,61,62,63,64,65,66,67,68,69,70,60,59,58,57,56,55,54,53,52,51,41,42,43,44,45,46,47,48,49,50,40,39,38,37,36,35,34,33,32,31,21,22,23,24,25,26,27,28,29,30,20,19,18,17,16,15,14,13,12,11,1,2,3,4,5,6,7,8,9,10] 94 | div(class="tile tile"+val, data-index=val) 95 | svg#snakeladders(viewBox="0 0 600 600", preserveAspectRatio="none") 96 | g.ladders-big 97 | path(d="M89,574 140,392 M102,578 153,396 M388,581 385,508 M402,580 399,507 M449,552 554,395 M460,560 565,403 M320,516 333,444 M334,518 347,446 M24,436 74,329 M36,442 87,334 M430,458 210,99 M441,450 222,93 M264,393 214,339 M273,384 224,330 M563,284 391,203 M568,271 396,190 M570,149 559,31 M584,148 573,30 M142,158 155,40 M156,160 169,42 M397,101 379,30 M411,97 392,26") 98 | g.ladders-small 99 | path(d="M91,568 104,573 M94,556 105,561 M97,544 108,549 M100,532 111,537 M103,520 114,525 M107,508 118,513 M111,496 122,501 M114,484 125,489 M117,472 128,477 M121,460 132,465 M124,448 135,453 M128,436 139,441 M131,424 142,429 M134,412 145,417 M138,400 149,405 M388,575 401,574 M388,563 401,562 M388,552 401,551 M388,541 401,540 M388,530 400,529 M388,518 400,517 M452,546 464,555 M459,536 470,544 M466,526 476,533 M473,516 482,522 M479,508 490,515 M485,498 496,506 M492,488 502,495 M498,479 509,486 M504,469 515,477 M512,459 522,467 M517,449 528,457 M523,440 534,448 M530,430 541,438 M536,421 548,429 M542,412 554,419 M549,402 560,409 M322,510 335,512 M324,498 337,500 M326,486 339,488 M328,475 341,477 M330,463 343,465 M332,452 345,454 M258,388 268,378 M250,379 260,369 M243,370 252,361 M235,362 245,352 M227,353 236,344 M219,345 229,335 M27,430 40,436 M32,419 45,425 M37,408 49,414 M42,398 54,404 M47,388 59,394 M51,377 64,383 M56,366 69,372 M61,356 75,362 M66,345 79,351 M71,335 84,341 M427,453 438,444 M420,443 432,434 M414,433 426,424 M408,423 420,414 M402,413 413,404 M395,402 407,393 M388,391 400,382 M381,379 393,370 M374,368 386,359 M367,356 378,347 M359,344 371,336 M352,332 364,324 M345,320 357,312 M338,308 350,300 M330,296 342,288 M323,284 335,276 M316,272 328,264 M308,260 320,252 M301,248 313,240 M294,236 305,228 M287,224 298,216 M280,212 291,205 M273,200 284,193 M267,188 277,181 M259,175 269,168 M251,163 261,156 M244,151 254,144 M236,139 246,132 M229,127 239,120 M222,115 232,108 M215,103 225,96 M143,152 157,154 M144,140 158,142 M145,128 159,130 M146,116 160,118 M147,104 161,106 M149,92 163,94 M150,80 164,82 M151,68 165,70 M153,56 167,58 M154,44 168,46 M396,95 409,91 M393,84 406,80 M390,73 403,69 M387,62 400,58 M384,51 397,47 M381,40 394,36 M570,143 583,142 M568,130 582,129 M566,117 580,116 M565,104 577,103 M564,91 576,90 M563,78 575,77 M562,65 574,64 M561,52 573,51 M560,38 572,37 M556,281 562,268 M546,276 552,263 M536,271 542,258 M526,266 532,254 M516,261 522,249 M506,255 512,244 M496,250 502,239 M486,245 492,235 M476,240 482,230 M466,236 472,225 M456,231 462,220 M446,226 451,216 M436,221 441,211 M425,218 431,206 M415,213 421,201 M405,208 411,196 M395,204 401,192") 100 | path(d="M20,42 28,42 28,35 32,35 32,42 40,42 40,32 45,32 30,20 15,32 20,32 Z M20,578 30,578 30,583 40,570 30,557 30,562 20, 562Z", fill="rgba(0,0,0,0.25)") 101 | path.snake(d="M 90,226 C 98,257 69,322 128,332 187,342 181,381 120,382 59,383 53,425 54,433 56,450 87,467 87,481 87,495 76,506 76,512 84,505 95,496 96,480 97,464 60,447 64,425 68,403 99,392 134,391 169,390 219,344 131,322 94,313 100,279 101,256 102,233 101,229 100,225 105,223.62 106,214 96,210 86,205 65.75,211 83,223 85.88,225 88,226 90,226 Z", fill="#73880A") 102 | circle.eye(cx=97, cy=212, r=5) 103 | path.snake(d="M 22,282 C 35,271 58,266 81,273 104,280 144,278 145,253 146,228 175,208 193,213 204,198 220,210 220,216 220,222 201,229 193,221 182,222 157,229 156,252 155,275 139,297 86,281 33,268 22,283 22,283 Z", fill="#D15600") 104 | circle.eye(cx=203, cy=210, r=5) 105 | path.snake(d="M 453,271 C 433,285 396,289 400,261 405,233 394,223 369,223 343,224 319,167 376,161 379,168 401,168 402,156 403,143 376,143 375,152 300,162 333,233 366,233 402,233 388,258 389,266 390,281 414,309 453,272 Z", fill="#6BBA70") 106 | circle.eye(cx=387, cy=148, r=5) 107 | path.snake(d="M 457,199 C 429,161 465,134 489,158 512,182 542,159 540,129 538,99 523,98 522,98 528,91 505,75 495,97 503,113 518,105 519,105 519,105 530,110 531,130 532,150 513,169 492,149 471,129 445,143 443,159 442,174 449,194 457,200 Z", fill="#C79810") 108 | circle.eye(cx=512, cy=90, r=5) 109 | path.snake(d="M 441,95 C 457,93 479,72 445,52 415,33 434,16 451,16 468,16 489,39 492,40 495,28 521,33 521,43 521,54 496,54 490,48 490,48 466,23 450,25 434,27 439,43 459,51 479,61 473,101 441,96 Z", fill="#356AA0") 110 | circle.eye(cx=504, cy=35, r=5) 111 | path.snake(d="M 278,457 C 291,438 262,428 247,444 230,464 193,450 231,420 256,400 300,425 335,395 349,381 334,338 334,338 334,338 348,325 325,324 301,322 306,341 326,340 334,358 346,401 285,401 231,401 205,416 205,446 206,472 244,462 250,452 258,441 281,435 278,458 Z", fill="#3F4C6B") 112 | circle.eye(cx=327, cy=325, r=5) 113 | path.snake(d="M 343,584 C 352,567 344,553 320,553 296,553 286,562 279,568 272,573 238,592 238,558 237,524 253,517 253,517 253,517 270,517 270,509 270,498 238,501 247,514 227,537 230,556 230,561 229,567 239,605 281,575 324,544 349,562 343,585 Z", fill="#008C00") 114 | circle.eye(cx=257, cy=503, r=5) 115 | path.snake(d="M 572,506 C 555,514 511,516 518,456 526,396 518,380 490,388 457,397 436,346 492,335 507,341 518,334 518,326 518,317 494,313 489,326 422,337 448,410 489,398 525,384 510,449 510,456 510,462 502,547 572,508 Z", fill="#D01F3C") 116 | circle.eye(cx=503, cy=320, r=5) 117 | path.snake(d="M 19,138 C 45,154 84,152 63,119 43,87 59,85 79,84 99,83 103,77 94,40 85,46 73,39 72,32 72,25 109,13 102,35 117,69 108,100 79,94 52,88 70,114 73,118 82,127 83,190 18,138 Z", fill="#2096cE") 118 | circle.eye(cx=89, cy=27, r=5) 119 | path.snake(d="M 322,152 C 302,168 282,153 285,137 287,120 287,121 275,110 263,99 295,79 291,62 287,45 297,30 318,38 324,51 346,48 346,39 347,30 327,20 320,31 295,18 274,33 280,57 286,82 252,86 262,111 265,118 280,122 275,137 271,148 291,185 322,154 Z", fill="#CC4444") 120 | circle.eye(cx=330, cy=30, r=5) 121 | each player in [1,2,3,4] 122 | svg.piece(id="piece-player-"+player, viewBox="0 0 100 100") 123 | g 124 | path(d="M 50,1 C 50,1 80,85 80,85 70,96 60,99 50,99 40,99 30,96 20,85 20,85 50,1 50,1 Z") 125 | circle(cx=50, cy=21, r=20) 126 | rect(x=44, y=25, width=12, height=22, stroke="none") 127 | 128 | #snake-popup.scrim 129 | div.box 130 | h1 Snake!!! 131 | div Oh no! The snake bit you! You'll have to move your piece to the snake's tail. 132 | div 133 | each players in [1,2,3,4] 134 | each val, index in {16:6, 46:25, 49:11, 62:19, 64:60, 74:53, 89:68, 92:88, 95:75, 99:80} 135 | label(id="snake-pl"+players+"-"+index, for="cb-pl"+players+"-"+val) Move Down 136 | svg(viewBox="0 0 100 100") 137 | path(d="M1,32 4,32 M6,34 4,32 M4,32 10,20", stroke="black", stroke-width="1") 138 | path(d="M 30,70 C 49,56 60,84 33,92 -17,103 0,50 24,45 32,44 37,41 29,27 26,33 5,35 3,24 4,13 26,3 37,16 49,30 49,52 28,55 8,59 0,94 33,82 56,72 35,66 30,71 Z", fill="#73B80A", stroke-width="0") 139 | circle(cx=18, cy=15, r=5, fill="black", stroke="white", stroke-width=6) 140 | 141 | 142 | #ladder-popup.scrim 143 | div.box 144 | h1 Ladder! 145 | div Yay! You found a ladder! You can climb up now. 146 | div 147 | each players in [1,2,3,4] 148 | each val, index in {2:38, 7:14, 8:31, 15:26, 21:42, 28:84, 36:44, 51:67, 71:91, 78:98, 87:94} 149 | label(id="ladder-pl"+players+"-"+index, for="cb-pl"+players+"-"+val) Move Up 150 | svg(viewBox="0 0 100 100") 151 | path(d="M5,85 20,10", stroke="#8B4513", stroke-width="10", "stroke-linecap"="round") 152 | path(d="M10,67 30,71 M15,45 35,49 M20,23 40,27", stroke="#aB6533", stroke-width="5", "stroke-linecap"="round") 153 | path(d="M30,90 45,15", stroke="#8B4513", stroke-width="10", "stroke-linecap"="round") 154 | 155 | #home-popup.scrim 156 | div.box 157 | h1 Home 158 | div You made it home, but you didn't do it in an exact jump so you will bounce back the remaining tiles. 159 | div 160 | each players in [1,2,3,4] 161 | each val, index in {101:99, 102:98, 103:97, 104:96, 105:95, 106:94} 162 | label(id="home-pl"+players+"-"+index, for="cb-pl"+players+"-"+val) Bounce back 163 | svg(viewBox="0 0 100 100") 164 | path(d="M12,85 12,40 5,40 30,5 55,40 48,40 48,85 35,85 35,60 25,60 25,85 Z", fill="#8Ba5f3") 165 | path(d="M30,65 C 30,60 25,55 20,55 15,55 10,60 10,65 10,70 15,75 20,75 20,75 20,75 50,75 M40,65 50,75 40,85", stroke="#eeee60", stroke-width=6, fill="none") 166 | 167 | #congratulations.scrim 168 | div.box 169 | h1 Congratulations! 170 | div You made it home safely! You won! 171 | div Reload the page to play again. 172 | svg(viewBox="0 0 100 100") 173 | path(d="M5,10 C 5,10 65,10 65,10 65,10 65,40 65,40 65,50 55,70 35,70 15,70 5,50 5,40 Z", fill="#fede60") 174 | path(d="M35,40 35,85 M20,85 50,85", stroke="#fede60", stroke-width=12, fill="none", stroke-linecap="round") 175 | 176 | div#fx-dice.fx-sound 177 | embed(src="https://studiokah.com/tests/dice.mp3") 178 | div#fx-click.fx-sound 179 | // Click sound by Sebastian. Source: http://soundbible.com/1705-Click2.html 180 | embed(src="https://studiokah.com/tests/click2.mp3") -------------------------------------------------------------------------------- /styles.scss: -------------------------------------------------------------------------------- 1 | // variables 2 | $border-radius: 3px; 3 | $color-blue: #48c; 4 | $color-red: #f55; 5 | $color-green: #4a5; 6 | $color-yellow: #dd5; 7 | $animation-time: 0.6s; 8 | $ladders: (2, 7, 8, 15, 21, 28, 36, 51, 71, 78, 87); 9 | $snakes: (16, 46, 49, 62, 64, 74, 89, 92, 95, 99); 10 | 11 | /***** dice animation */ 12 | @keyframes changeOrder { 13 | from { z-index: 6;} 14 | to { z-index: 1; } 15 | } 16 | 17 | @-webkit-keyframes changeOrder { 18 | from { z-index: 6; } 19 | to { z-index: 1; } 20 | } 21 | 22 | // coment for testing 23 | input { display: none; } 24 | 25 | .fx-sound { 26 | display: none; 27 | position: absolute; 28 | top: -1000px; 29 | left: -1000px; 30 | height: 0px; 31 | width: 0px; 32 | overflow: hidden; 33 | } 34 | #play-fx:checked ~ { 35 | label.dice:active ~ #fx-dice { display: block; } 36 | #game label:active ~ .fx-sound { display: block; } 37 | } 38 | 39 | 40 | // presentation 41 | @import url('https://fonts.googleapis.com/css?family=Dosis|Roboto'); 42 | 43 | body { 44 | font-size: 16px; 45 | font-family: Dosis, Roboto, Arial, Verdana, sans-serif; 46 | padding: 0; 47 | margin: 0; 48 | border: 0; 49 | background: rgba(128,200,255,0.8) /*rgba(128,200,255,0.8)*/; 50 | } 51 | 52 | .scrim { 53 | position: fixed; 54 | top: 0; 55 | left: 0; 56 | width: 100%; 57 | height: 100%; 58 | background: rgba(0,0,0,0.75); 59 | z-index: 999; 60 | .box { 61 | position: absolute; 62 | top: 50%; 63 | left: 50%; 64 | transform: translate(-50%, -50%); 65 | -webkit-transform: translate(-50%, -50%); 66 | background: white; 67 | border-radius: $border-radius; 68 | box-shadow: 0 1.5em 0.5em -1em rgba(0, 0, 0, 0.25); 69 | width: 70%; 70 | max-width: 375px; 71 | > div { 72 | padding:1rem; 73 | } 74 | h1 { 75 | margin-top: 0; 76 | margin-bottom: 0; 77 | font-size: 1.75rem; 78 | text-align: center; 79 | height: 2.5rem; 80 | line-height: 2.35rem; 81 | background: $color-blue; 82 | color: white; 83 | border-radius: $border-radius $border-radius 0px 0px; 84 | } 85 | h2 { 86 | margin-top: 1rem; 87 | margin-bottom: 0.15rem; 88 | font-size: 1.3rem; 89 | &:first-child { 90 | margin-top: 0; 91 | } 92 | } 93 | p:first-child { 94 | margin-top: 0; 95 | } 96 | p:last-child { 97 | margin-bottom: 0; 98 | } 99 | } 100 | } 101 | 102 | .flex { 103 | display: flex; 104 | > label, > div { 105 | flex: 1; 106 | margin-left: 0.3rem; 107 | &:first-child { 108 | margin-left: 0; 109 | } 110 | } 111 | } 112 | 113 | /** start screen: configuration **/ 114 | #options { 115 | background: rgba(0,0,0,0); 116 | .option-players label, label.option-start { 117 | display: block; 118 | margin-top: 0.5rem; 119 | color: white; 120 | background: rgba(0,150,48,0.6); 121 | text-align: center; 122 | padding: 6px; 123 | transition: background 0.5s, color 0.5s; 124 | box-sizing: border-box; 125 | border: 2px solid transparent; 126 | border-radius: $border-radius; 127 | cursor: pointer; 128 | &:hover { 129 | background: rgba(220,220,0,0.7); 130 | color: black; 131 | } 132 | &:active { 133 | border: 2px solid rgba(0,0,0,0.1); 134 | } 135 | &.option-start { 136 | background: $color-blue; 137 | color: white; 138 | margin-top: 2rem; 139 | &:hover { 140 | background: darken($color-blue, 5%); 141 | } 142 | } 143 | } 144 | .option-colors label { 145 | opacity: 0.6; 146 | margin-bottom: 0.3rem; 147 | cursor: pointer; 148 | //transition: opacity 0.5s; // commented as it creates weird jump 149 | &:hover { 150 | opacity: 0.8; 151 | } 152 | span { 153 | flex: 1; 154 | margin-left: 0.3rem; 155 | border-radius: $border-radius; 156 | display: inline-block; 157 | padding: 3px; 158 | font-size: 0.7rem; 159 | border: 2px solid transparent; 160 | &:first-child { 161 | flex: 0; 162 | margin-left: 0; 163 | background: none; 164 | font-size: 1rem; 165 | &::before { 166 | content: "\2610"; 167 | } 168 | } 169 | } 170 | &:nth-child(1) span { 171 | &:nth-child(2) { background: $color-green; } 172 | &:nth-child(3) { background: $color-blue; } 173 | &:nth-child(4) { background: $color-red; } 174 | &:nth-child(5) { background: $color-yellow; } 175 | } 176 | &:nth-child(2) span { 177 | &:nth-child(2) { background: $color-blue; } 178 | &:nth-child(3) { background: $color-red; } 179 | &:nth-child(4) { background: $color-yellow; } 180 | &:nth-child(5) { background: $color-green; } 181 | } 182 | &:nth-child(3) span { 183 | &:nth-child(2) { background: $color-red; } 184 | &:nth-child(3) { background: $color-yellow; } 185 | &:nth-child(4) { background: $color-green; } 186 | &:nth-child(5) { background: $color-blue; } 187 | } 188 | &:nth-child(4) span { 189 | &:nth-child(2) { background: $color-yellow; } 190 | &:nth-child(3) { background: $color-green; } 191 | &:nth-child(4) { background: $color-blue; } 192 | &:nth-child(5) { background: $color-red; } 193 | } 194 | } 195 | #activate-fx { 196 | cursor: pointer; 197 | &::before { 198 | content: "\2610\00a0"; 199 | } 200 | } 201 | } 202 | 203 | // hide the game settings when Start is pressed 204 | #game-time:checked ~ #options, 205 | [name=show-instructions]:checked ~ #options { display: none; } 206 | #play-fx:checked ~ #options #activate-fx::before { content: "\2612\00a0" } 207 | 208 | // control active # of players and colors 209 | @for $i from 1 to 5 { 210 | #players#{$i}:checked { 211 | ~ #options { 212 | label[for=players#{$i}] { 213 | background: rgba(0,150,48,1); 214 | border: 2px solid rgba(0,0,0,0.1); 215 | color: white; 216 | } 217 | @for $j from $i+1 to 5 { 218 | label[for^=colors] span:nth-child(#{$j+1}){ 219 | display: none; 220 | } 221 | } 222 | } 223 | } 224 | #colors#{$i}:checked { 225 | ~ #options { 226 | label[for=colors#{$i}] { 227 | opacity: 1; 228 | border: 0; 229 | span { 230 | border: 2px solid rgba(0,0,0,0.1); 231 | &:first-child { 232 | border: 2px solid transparent; 233 | &::before { 234 | content:"\2612"; 235 | } 236 | } 237 | } 238 | } 239 | } 240 | } 241 | } 242 | 243 | // assign colors to the game pieces according to the color scheme selected 244 | // TODO: this probably can be reduced with SASS as well as the code above 245 | #colors1:checked ~ #game #piece-player-1 g { fill: $color-green; } 246 | #colors1:checked ~ #game #piece-player-2 g { fill: $color-blue; } 247 | #colors1:checked ~ #game #piece-player-3 g { fill: $color-red; } 248 | #colors1:checked ~ #game #piece-player-4 g { fill: $color-yellow; } 249 | #colors2:checked ~ #game #piece-player-1 g { fill: $color-blue; } 250 | #colors2:checked ~ #game #piece-player-2 g { fill: $color-red; } 251 | #colors2:checked ~ #game #piece-player-3 g { fill: $color-yellow; } 252 | #colors2:checked ~ #game #piece-player-4 g { fill: $color-green; } 253 | #colors3:checked ~ #game #piece-player-1 g { fill: $color-red; } 254 | #colors3:checked ~ #game #piece-player-2 g { fill: $color-yellow; } 255 | #colors3:checked ~ #game #piece-player-3 g { fill: $color-green; } 256 | #colors3:checked ~ #game #piece-player-4 g { fill: $color-blue; } 257 | #colors4:checked ~ #game #piece-player-1 g { fill: $color-yellow; } 258 | #colors4:checked ~ #game #piece-player-2 g { fill: $color-green; } 259 | #colors4:checked ~ #game #piece-player-3 g { fill: $color-blue; } 260 | #colors4:checked ~ #game #piece-player-4 g { fill: $color-red; } 261 | 262 | /** info pop-up **/ 263 | #info { 264 | display: none; 265 | .box { 266 | width: 75%; 267 | max-width: none; 268 | p:last-child { 269 | text-align: right; 270 | } 271 | #close-button { 272 | display: inline-block; 273 | background: $color-green; 274 | border-radius: $border-radius; 275 | color: white; 276 | padding: 6px 1rem; 277 | cursor: pointer; 278 | transition: background 0.5s; 279 | &:hover { 280 | background: darken($color-green, 5%); 281 | } 282 | } 283 | } 284 | } 285 | #show-info:checked { 286 | ~ #info { display: block; } 287 | } 288 | 289 | 290 | /** Instructions **/ 291 | #instructions { 292 | position: fixed; 293 | z-index: 9999; 294 | background-color: rgba(0,0,0,0.01); 295 | top: 0; 296 | left: 0; 297 | width: 100%; 298 | height: 100%; 299 | display: none; 300 | #go-to-game { 301 | position: absolute; 302 | bottom: 1rem; 303 | right: 1rem; 304 | font-size: 1rem; 305 | z-index: 3; 306 | color: white; 307 | cursor: pointer; 308 | } 309 | #step { 310 | display: none; 311 | position: absolute; 312 | box-shadow: 0 0 50px calc(200vmax) rgba(0,0,0,0.85), inset 0 0 4px 0 rgba(0,0,0,1); 313 | transform: translate(-1rem, -1rem); 314 | border-radius: 100%; 315 | z-index: 1; 316 | top: 0; 317 | left: 1rem; 318 | width: calc(100px + 2rem); 319 | height: calc(6rem); 320 | transition: all 0.75s; 321 | div { 322 | position: absolute; 323 | bottom: -0.5rem; 324 | line-height: 1.1rem; 325 | font-size: 1rem; 326 | color: rgba(255,255,255,0.8); 327 | left: 1rem; 328 | width: 80vmin; 329 | transform: translate(0, 100%); 330 | -webkit-transform: translate(0, 100%); 331 | label { 332 | display: none; 333 | font-weight: bold; 334 | cursor: pointer; 335 | text-decoration: underline; 336 | &:hover { 337 | text-decoration: none; 338 | } 339 | &:not([for=game-time])::before { 340 | content: "Next \203A" 341 | } 342 | } 343 | } 344 | } 345 | } 346 | 347 | [name=show-instructions] { 348 | &:checked ~ { 349 | #instructions, #game, #action, #instructions #step { 350 | display: block; 351 | } 352 | } 353 | } 354 | 355 | @for $i from 1 to 5 { 356 | #show-instructions-#{$i}:checked ~ #instructions label[for=show-instructions-#{$i+1}] { 357 | display: inline-block; 358 | } 359 | } 360 | #show-instructions-5:checked ~ #instructions label[for=game-time] { 361 | display: inline-block; 362 | } 363 | 364 | #show-instructions-1:checked ~ #instructions #step { 365 | top: 0; 366 | left: 1rem; 367 | width: calc(100px + 2rem); 368 | height: calc(6rem); 369 | div::before { 370 | content: "This area indicates whose turn it is at any given moment."; 371 | } 372 | } 373 | #show-instructions-2:checked ~ #instructions #step { 374 | top:6rem; 375 | left: 1rem; 376 | width: 5rem; 377 | height: 5rem; 378 | div::before { 379 | content: "The active player clicks here to roll the dice."; 380 | } 381 | } 382 | #show-instructions-3:checked ~ #instructions #step { 383 | top: 50%; 384 | left: 50%; 385 | width: 30vw; 386 | height: 30vw; 387 | max-width: 500px; 388 | max-height: 500px; 389 | transform: translate(-50%, -50%); 390 | -webkit-transform: translate(-50%, -50%); 391 | div { 392 | transform: translate(-20vmin, 100%); 393 | -webkit-transform: translate(-20vmin, 100%); 394 | left: 0; 395 | &::before { 396 | content: "The player's piece will advance on the board once the dice is rolled. If the piece falls on a tile with a snake's head, it will fall down to the snake's tail. If it falls on a ladder's bottom, it will go up to the top."; 397 | } 398 | } 399 | } 400 | #show-instructions-4:checked ~ #instructions #step { 401 | top: 0; 402 | left: calc(100px + 2rem); 403 | width: calc(100px + 2rem); 404 | height: calc(6rem); 405 | div::before { 406 | content: "After that, click on the button to go to the next player's turn."; 407 | } 408 | } 409 | #show-instructions-5:checked ~ #instructions #step { 410 | top: 0; 411 | left: calc(100vw - 100px - 1rem); 412 | width: calc(100px + 2rem); 413 | height: calc(6rem); 414 | div { 415 | left: auto; 416 | right: 0rem; 417 | &::before { 418 | content: "If you want more information during the game, click on this button." 419 | } 420 | } 421 | } 422 | 423 | #game-time:checked ~ #instructions { 424 | display: none; 425 | } 426 | 427 | /** game screen: board, dice, messages **/ 428 | .dice { 429 | position: absolute; 430 | display: none; 431 | visibility: visible; 432 | top: 6rem; 433 | left: 1rem; 434 | width: 3rem; 435 | height: 3rem; 436 | background: white; 437 | border-radius: $border-radius; 438 | box-shadow: 0 1.5em 0.5em -1em rgba(0, 0, 0, 0.042); 439 | z-index:1; 440 | &::before { 441 | content: "Roll the Dice"; 442 | display: block; 443 | height: 100%; 444 | width: 100%; 445 | font-size: 0.8rem; 446 | box-sizing: border-box; 447 | cursor: pointer; 448 | padding-top: 0.5rem; 449 | } 450 | } 451 | 452 | 453 | @for $i from 1 to 5 { 454 | #turn#{$i}:checked ~ .game-time:checked ~ [name=cb-player#{$i}]:checked + label + input + label, 455 | #turn#{$i}:checked ~ .game-time:checked ~ [name=cb-player#{$i}]:checked + label + input + label + input + label, 456 | #turn#{$i}:checked ~ .game-time:checked ~ [name=cb-player#{$i}]:checked + label + input + label + input + label + input + label, 457 | #turn#{$i}:checked ~ .game-time:checked ~ [name=cb-player#{$i}]:checked + label + input + label + input + label + input + label + input + label, 458 | #turn#{$i}:checked ~ .game-time:checked ~ [name=cb-player#{$i}]:checked + label + input + label + input + label + input + label + input + label + input + label, 459 | #turn#{$i}:checked ~ .game-time:checked ~ [name=cb-player#{$i}]:checked + label + input + label + input + label + input + label + input + label + input + label + input + label { 460 | display: block; 461 | left: 1rem; 462 | text-align: center; 463 | animation: changeOrder $animation-time infinite; 464 | -webkit-animation: changeOrder $animation-time infinite; 465 | } 466 | } 467 | 468 | @for $i from 1 to 5 { 469 | #turn#{$i}:checked ~ [name=cb-player#{$i}]:checked + label + input + label { animation-delay: 0s !important; } 470 | #turn#{$i}:checked ~ [name=cb-player#{$i}]:checked + label + input + label + input + label { animation-delay: -$animation-time/6 !important; } 471 | #turn#{$i}:checked ~ [name=cb-player#{$i}]:checked + label + input + label + input + label + input + label { animation-delay: -2*$animation-time/6 !important; } 472 | #turn#{$i}:checked ~ [name=cb-player#{$i}]:checked + label + input + label + input + label + input + label + input + label { animation-delay: -3*$animation-time/6 !important; } 473 | #turn#{$i}:checked ~ [name=cb-player#{$i}]:checked + label + input + label + input + label + input + label + input + label + input + label { animation-delay: -4*$animation-time/6 !important; } 474 | #turn#{$i}:checked ~ [name=cb-player#{$i}]:checked + label + input + label + input + label + input + label + input + label + input + label + input + label { animation-delay: -5*$animation-time/6 !important; } 475 | } 476 | 477 | @for $i from 1 to 5 { 478 | /* 479 | #turn#{$i}:checked ~ [name=cb-player#{$i}]:checked + label + input + label::before { background-image: 480 | url("data:image/svg+xml;utf8,"); 481 | } 482 | #turn#{$i}:checked ~ [name=cb-player#{$i}]:checked + label + input + label + input + label::before { background-image: 483 | url("data:image/svg+xml;utf8,"); } 484 | #turn#{$i}:checked ~ [name=cb-player#{$i}]:checked + label + input + label + input + label + input + label::before { background-image: 485 | url("data:image/svg+xml;utf8,"); } 486 | #turn#{$i}:checked ~ [name=cb-player#{$i}]:checked + label + input + label + input + label + input + label + input + label::before { background-image: 487 | url("data:image/svg+xml;utf8,"); } 488 | #turn#{$i}:checked ~ [name=cb-player#{$i}]:checked + label + input + label + input + label + input + label + input + label + input + label::before { background-image: 489 | url("data:image/svg+xml;utf8,"); } 490 | #turn#{$i}:checked ~ [name=cb-player#{$i}]:checked + label + input + label + input + label + input + label + input + label + input + label + input + label::before { background-image: 491 | url("data:image/svg+xml;utf8,"); } 492 | */ 493 | } 494 | 495 | 496 | // label:active adapted from solution by Temani Afif. Ref: https://stackoverflow.com/a/51451218/3695983 497 | label.dice:active { 498 | position:static !important; 499 | margin-left: 50px !important; 500 | background: none !important; 501 | font-size: 0; 502 | &::before { 503 | content:""; 504 | position:absolute; 505 | top:0; 506 | right:-50px; 507 | left:0; 508 | bottom:0; 509 | z-index:200; 510 | } 511 | } 512 | 513 | #game { 514 | display: none; 515 | .tab { 516 | position: absolute; 517 | top: 0; 518 | background: white; 519 | border-radius: 0 0 3px 3px; 520 | box-shadow: 0 1.5em 0.5em -1em rgba(0, 0, 0, 0.25); 521 | z-index: 9; 522 | float:left; 523 | margin-left: 1rem; 524 | &::before { 525 | text-align: center; 526 | display: block; 527 | padding: 6px 16px; 528 | color: white; 529 | background: $color-blue; 530 | height: 0.7rem; 531 | line-height: 0.7rem; 532 | font-size: 0.75rem; 533 | font-weight: bold; 534 | white-space: nowrap; 535 | text-transform: uppercase; 536 | font-size: 0.95rem; 537 | } 538 | &::after, &#player-info span { 539 | display: block; 540 | text-align: center; 541 | padding: 6px 16px; 542 | height: 2rem; 543 | line-height: 1.9rem; 544 | white-space: nowrap; 545 | } 546 | &#player-info { 547 | min-width: 100px; 548 | &::before { content: "Turn"; } 549 | span { 550 | font-weight: bolder; 551 | &::before { 552 | content: "\25CF\00A0"; 553 | } 554 | &::after { 555 | content: " 1"; 556 | font-weight: bolder; 557 | } 558 | } 559 | } 560 | &#dice { 561 | cursor: pointer; 562 | &::before { content: "Dice"; } 563 | &::after { 564 | content: "Roll"; 565 | } 566 | } 567 | &#more-info { 568 | cursor: pointer; 569 | left: auto; 570 | right: 1rem; 571 | width: 100px; 572 | z-index: 1; 573 | &::before { content: "About"; } 574 | &::after { content: "More info" } 575 | } 576 | &#action { 577 | cursor: pointer; 578 | left: calc(100px + 1rem); 579 | width: 100px; 580 | &::before { content: "Action"; } 581 | &::after { content: "Next player" } 582 | label { 583 | position: absolute; 584 | top: 0; 585 | left: 0; 586 | bottom: 0; 587 | right: 0; 588 | cursor: pointer; 589 | } 590 | } 591 | } 592 | #board { 593 | width: 70%; 594 | max-width: 500px; 595 | border: 3px solid $color-blue; 596 | border-radius: $border-radius * 2; 597 | position: absolute; 598 | top: 50%; 599 | left: 50%; 600 | transform: translate(-50%, -50%); 601 | -webkit-transform: translate(-50%, -50%); 602 | background: white; 603 | margin-top: 2.5rem; 604 | box-shadow: 0 1.5em 0.5em -1em rgba(0, 0, 0, 0.25); 605 | .tile { 606 | float: left; 607 | width: 10%; 608 | position: relative; 609 | height: 0; 610 | padding-top: 10%; 611 | box-sizing: border-box; 612 | border-left: 1px solid rgba(0,0,0,0.1); 613 | border-right: 1px solid rgba(0,0,0,0.1); 614 | border-top: 1px solid rgba(0,0,0,1); 615 | border-bottom: 1px solid rgba(0,0,0,1); 616 | background: lighten(#9ad0ac,15%); 617 | &::before { 618 | content: attr(data-index); 619 | position: absolute; 620 | top: 0rem; 621 | left: 0.15rem; 622 | font-size: 0.66rem; 623 | } 624 | &.tile6, &.tile12, &.tile18, &.tile22, &.tile23, &.tile28, &.tile30, &.tile34, &.tile36, &.tile39, &.tile55, &.tile79 { 625 | &::before { 626 | left: auto; 627 | right: 0.15rem; 628 | } 629 | } 630 | &.tile52::before { 631 | top:auto; 632 | bottom:0rem; 633 | } 634 | &.tile76::before { 635 | left: 0.6rem; 636 | } 637 | } 638 | @for $i from 1 to 11 { 639 | .tile#{$i} { border-bottom: 1px solid transparent; } 640 | .tile#{$i+90} { border-top: 1px solid transparent; } 641 | .tile#{$i*10} { border-top: 1px solid rgba(0,0,0,0.1); } 642 | .tile#{$i*10+1} { border-bottom: 1px solid rgba(0,0,0,0.1); } 643 | } 644 | .tile1, .tile15, .tile24, .tile38, .tile49, .tile55, .tile65, .tile76, .tile86, .tile94 { 645 | background: lighten(#f198b4, 15%); 646 | } 647 | 648 | .tile2, .tile16, .tile23, .tile35, .tile42, .tile56, .tile64, .tile79, .tile87, .tile93 { 649 | background: lighten(#C3D9FF, 8%); 650 | } 651 | 652 | .tile3, .tile18, .tile26, .tile33, .tile41, .tile58, .tile68, .tile78, .tile81, .tile96 { 653 | background: lighten(#f89448, 30%); 654 | } 655 | 656 | .tile4, .tile11, .tile29, .tile40, .tile47, .tile51, .tile61, .tile77, .tile88, .tile99 { 657 | background: lighten(#c297c5, 23%); 658 | } 659 | 660 | .tile5, .tile19, .tile27, .tile32, .tile44, .tile59, .tile69, .tile80, .tile89, .tile97 { 661 | background: lighten(#68caef, 18%); 662 | } 663 | 664 | .tile6, .tile20, .tile28, .tile31, .tile45, .tile60, .tile70, .tile75, .tile85, .tile98 { 665 | background: lighten(#fef471, 21%); 666 | } 667 | 668 | .tile7, .tile13, .tile21, .tile36, .tile50, .tile53, .tile63, .tile74, .tile84, .tile91 { 669 | background: lighten(#b9e6fd, 5%); 670 | } 671 | 672 | .tile8, .tile12, .tile30, .tile39, .tile46, .tile52, .tile62, .tile73, .tile90, .tile100 { 673 | background: lighten(#e5b75f,19%); 674 | } 675 | 676 | .tile9, .tile14, .tile22, .tile37, .tile48, .tile54, .tile66, .tile72, .tile83, .tile96 { 677 | background: lighten(#f37859,20%); 678 | } 679 | } 680 | } 681 | 682 | #action label { display: none; } 683 | #players1:checked ~ #turn1:checked ~ #game-time:checked ~ #game #action { display:none; } 684 | #players1:checked ~ #turn1:checked ~ #game #turn-changer1 { display: block; } 685 | #players2:checked ~ #turn1:checked ~ #game #turn-changer2 { display: block; } 686 | #players3:checked ~ #turn1:checked ~ #game #turn-changer2 { display: block; } 687 | #players4:checked ~ #turn1:checked ~ #game #turn-changer2 { display: block; } 688 | #players2:checked ~ #turn2:checked ~ #game #turn-changer1 { display: block; } 689 | #players3:checked ~ #turn2:checked ~ #game #turn-changer3 { display: block; } 690 | #players4:checked ~ #turn2:checked ~ #game #turn-changer3 { display: block; } 691 | #players3:checked ~ #turn3:checked ~ #game #turn-changer1 { display: block; } 692 | #players4:checked ~ #turn3:checked ~ #game #turn-changer4 { display: block; } 693 | #players4:checked ~ #turn4:checked ~ #game #turn-changer1 { display: block; } 694 | 695 | // assign colors to the game pieces according to the color scheme selected 696 | // TODO: this probably can be reduced with SASS as well as the code above for piece colors 697 | #turn1:checked ~ #colors1:checked ~ #game #player-info span::before { color: $color-green; } 698 | #turn2:checked ~ #colors1:checked ~ #game #player-info span::before { color: $color-blue; } 699 | #turn3:checked ~ #colors1:checked ~ #game #player-info span::before { color: $color-red; } 700 | #turn4:checked ~ #colors1:checked ~ #game #player-info span::before { color: $color-yellow; } 701 | #turn1:checked ~ #colors2:checked ~ #game #player-info span::before { color: $color-blue; } 702 | #turn2:checked ~ #colors2:checked ~ #game #player-info span::before { color: $color-red; } 703 | #turn3:checked ~ #colors2:checked ~ #game #player-info span::before { color: $color-yellow; } 704 | #turn4:checked ~ #colors2:checked ~ #game #player-info span::before { color: $color-green; } 705 | #turn1:checked ~ #colors3:checked ~ #game #player-info span::before { color: $color-red; } 706 | #turn2:checked ~ #colors3:checked ~ #game #player-info span::before { color: $color-yellow; } 707 | #turn3:checked ~ #colors3:checked ~ #game #player-info span::before { color: $color-green; } 708 | #turn4:checked ~ #colors3:checked ~ #game #player-info span::before { color: $color-blue; } 709 | #turn1:checked ~ #colors4:checked ~ #game #player-info span::before { color: $color-yellow; } 710 | #turn2:checked ~ #colors4:checked ~ #game #player-info span::before { color: $color-green; } 711 | #turn3:checked ~ #colors4:checked ~ #game #player-info span::before { color: $color-blue; } 712 | #turn4:checked ~ #colors4:checked ~ #game #player-info span::before { color: $color-red; } 713 | 714 | @for $i from 1 to 5 { 715 | #turn#{$i}:checked ~ #game .tab#player-info span::after{ 716 | content: " #{$i}"; 717 | } 718 | } 719 | 720 | // show the game board when Start is pressed 721 | #game-time:checked ~ #game { 722 | display: block; 723 | } 724 | 725 | svg { 726 | &#snakeladders{ 727 | position: absolute; 728 | top: 0; 729 | left: 0; 730 | width: 100%; 731 | height: 100%; 732 | g { 733 | &.ladders-big { 734 | stroke: #8B4513; 735 | stroke-width: 4; 736 | stroke-linecap: round; 737 | } 738 | &.ladders-small { 739 | stroke: #8B4513; 740 | stroke-width: 2.5; 741 | stroke-linecap: round; 742 | } 743 | } 744 | } 745 | .snake { 746 | stroke: none; 747 | } 748 | .eye { 749 | fill: black; 750 | stroke: white; 751 | stroke-width: 6; 752 | } 753 | &.piece { 754 | position: absolute; 755 | width: 8%; 756 | bottom: 0; 757 | left: 0; 758 | transform: translate(-2%, -2%); 759 | -webkit-transform: translate(-2%, -2%); 760 | transition: bottom 0.5s, left 0.5s; 761 | g { 762 | fill: $color-red; 763 | stroke-width: 4; 764 | stroke: rgba(0,0,0,0.3); 765 | } 766 | &#piece-player-1 { 767 | transform: translate(-5%, -3%); 768 | -webkit-transform: translate(-5%, -3%); 769 | z-index:44; 770 | g { 771 | fill: $color-red; 772 | } 773 | } 774 | &#piece-player-2 { 775 | transform: translate(40%, -40%); 776 | -webkit-transform: translate(40%, -40%); 777 | z-index: 41; 778 | g { 779 | fill: $color-blue; 780 | } 781 | } 782 | &#piece-player-3 { 783 | transform: translate(42%, -7%); 784 | -webkit-transform: translate(42%, -7%); 785 | z-index: 43; 786 | g { 787 | fill: $color-green; 788 | } 789 | } 790 | &#piece-player-4 { 791 | transform: translate(-3%, -41%); 792 | -webkit-transform: translate(-3%, -41%); 793 | z-index: 42; 794 | g { 795 | fill: $color-yellow; 796 | } 797 | } 798 | } 799 | } 800 | 801 | // generate the different positions on each tile 802 | $positions: 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 90, 80, 70, 60, 50, 40, 30, 20, 10, 0; 803 | @for $i from 1 through 4 { 804 | #cb-pl#{$i+"-0"}:checked ~ #game #piece-player-#{$i} { 805 | left: -10%; 806 | bottom: 0; 807 | } 808 | @for $j from 1 to 101 { 809 | #cb-pl#{$i}-#{$j}:checked { 810 | ~ #game #piece-player-#{$i} { 811 | left: nth($positions, (($j - 1) % 20) + 1) * 1%; 812 | bottom: nth($positions, floor(($j - 1) / 10) + 1) * 1%; 813 | } 814 | } 815 | } 816 | @for $j from 101 to 107 { 817 | #cb-pl#{$i}-#{$j}:checked { 818 | ~ #game #piece-player-#{$i} { 819 | left: 0; 820 | bottom: 90%; 821 | } 822 | } 823 | } 824 | } 825 | 826 | // hide the players that are not active 827 | @for $i from 1 to 5 { 828 | @for $j from $i+1 to 5 { 829 | #players#{$i}:checked ~ #game #piece-player-#{$j} { 830 | display: none !important; 831 | } 832 | } 833 | } 834 | 835 | 836 | // all these popups have a similar code below that could be reduced with a mixin (TODO) 837 | #snake-popup, #ladder-popup, #home-popup, #congratulations { 838 | opacity: 0; 839 | z-index: -1; 840 | transition: opacity 0s; 841 | label { 842 | display: none; 843 | } 844 | svg { 845 | height:100%; 846 | position:absolute; 847 | right:0; 848 | top:0; 849 | transform:translate(80%, 0); 850 | } 851 | } 852 | 853 | // when we fall in a ladder, show the ladder popup 854 | @each $l in $ladders { 855 | .cb[id$='-#{$l}'] { 856 | &:checked { 857 | ~ .dice { 858 | display: none !important; 859 | } 860 | ~ #ladder-popup { 861 | opacity: 1; 862 | z-index: 99999; 863 | transition: opacity 0.15s; 864 | transition-delay: 0.5s; 865 | -webkit-transition-delay: 0.5s; 866 | label { 867 | background: $color-green; 868 | border-radius: $border-radius; 869 | color: white; 870 | padding: 6px 1rem; 871 | cursor: pointer; 872 | transition: background 0.5s; 873 | margin-top: 1rem; 874 | &:hover { 875 | background: darken($color-green, 5%); 876 | } 877 | } 878 | } 879 | } 880 | } 881 | } 882 | 883 | // when we fall in a ladder, show the ladder popup 884 | @each $s in $snakes { 885 | .cb[id$='-#{$s}'] { 886 | &:checked { 887 | ~ .dice { 888 | display: none !important; 889 | } 890 | ~ #snake-popup { 891 | opacity: 1; 892 | z-index: 99999; 893 | transition: opacity 0.15s; 894 | transition-delay: 0.5s; 895 | -webkit-transition-delay: 0.5s; 896 | label { 897 | background: $color-green; 898 | border-radius: $border-radius; 899 | color: white; 900 | padding: 6px 1rem; 901 | cursor: pointer; 902 | transition: background 0.5s; 903 | margin-top: 1rem; 904 | &:hover { 905 | background: darken($color-green, 5%); 906 | } 907 | } 908 | } 909 | } 910 | } 911 | } 912 | 913 | // when we fall in a ladder, show the ladder popup 914 | @for $s from 101 to 107 { 915 | .cb[id$='-#{$s}'] { 916 | &:checked { 917 | ~ .dice { 918 | display: none !important; 919 | } 920 | ~ #home-popup { 921 | opacity: 1; 922 | z-index: 99999; 923 | transition: opacity 0.15s; 924 | transition-delay: 0.5s; 925 | -webkit-transition-delay: 0.5s; 926 | label { 927 | background: $color-green; 928 | border-radius: $border-radius; 929 | color: white; 930 | padding: 6px 1rem; 931 | cursor: pointer; 932 | transition: background 0.5s; 933 | margin-top: 1rem; 934 | &:hover { 935 | background: darken($color-green, 5%); 936 | } 937 | } 938 | } 939 | } 940 | } 941 | } 942 | 943 | // show the right button in the ladder/snake popup 944 | @for $i from 1 to 5 { 945 | @each $l in $ladders { 946 | #cb-pl#{$i}-#{$l}:checked ~ #ladder-popup #ladder-pl#{$i}-#{$l} { display: inline-block; } 947 | } 948 | @each $s in $snakes { 949 | #cb-pl#{$i}-#{$s}:checked ~ #snake-popup #snake-pl#{$i}-#{$s} { display: inline-block; } 950 | } 951 | @for $j from 101 to 107 { 952 | #cb-pl#{$i}-#{$j}:checked ~ #home-popup #home-pl#{$i}-#{$j} { display: inline-block; } 953 | } 954 | } 955 | 956 | .cb[id$='-100']:checked { 957 | ~ #congratulations { 958 | opacity: 1; 959 | z-index: 99999; 960 | transition: opacity 0.15s; 961 | transition-delay: 0.5s; 962 | -webkit-transition-delay: 0.5s; 963 | } 964 | } 965 | 966 | @media (max-width: 400px) { 967 | #game { 968 | #board .tile::before { 969 | font-size: 0.4rem; 970 | } 971 | .tab { 972 | &::before { 973 | font-size: 0.65rem; 974 | } 975 | &::after, &#player-info span { 976 | font-size: 0.9rem; 977 | height: 1.5rem; 978 | line-height: 1.4rem; 979 | } 980 | } 981 | } 982 | } 983 | -------------------------------------------------------------------------------- /animated-dice/styles.scss: -------------------------------------------------------------------------------- 1 | // variables 2 | $border-radius: 3px; 3 | $color-blue: #48c; 4 | $color-red: #f55; 5 | $color-green: #4a5; 6 | $color-yellow: #dd5; 7 | $animation-time: 0.6s; 8 | $ladders: (2, 7, 8, 15, 21, 28, 36, 51, 71, 78, 87); 9 | $snakes: (16, 46, 49, 62, 64, 74, 89, 92, 95, 99); 10 | 11 | /***** dice animation */ 12 | @keyframes changeOrder { 13 | from { z-index: 6;} 14 | to { z-index: 1; } 15 | } 16 | 17 | @-webkit-keyframes changeOrder { 18 | from { z-index: 6; } 19 | to { z-index: 1; } 20 | } 21 | 22 | // coment for testing 23 | input { display: none; } 24 | 25 | .fx-sound { 26 | display: none; 27 | position: absolute; 28 | top: -1000px; 29 | left: -1000px; 30 | height: 0px; 31 | width: 0px; 32 | overflow: hidden; 33 | } 34 | #play-fx:checked ~ { 35 | label.dice:active ~ #fx-dice { display: block; } 36 | #game label:active ~ .fx-sound { display: block; } 37 | } 38 | 39 | 40 | // presentation 41 | @import url('https://fonts.googleapis.com/css?family=Dosis|Roboto'); 42 | 43 | body { 44 | font-size: 16px; 45 | font-family: Dosis, Roboto, Arial, Verdana, sans-serif; 46 | padding: 0; 47 | margin: 0; 48 | border: 0; 49 | background: rgba(128,200,255,0.8) /*rgba(128,200,255,0.8)*/; 50 | } 51 | 52 | .scrim { 53 | position: fixed; 54 | top: 0; 55 | left: 0; 56 | width: 100%; 57 | height: 100%; 58 | background: rgba(0,0,0,0.75); 59 | z-index: 999; 60 | .box { 61 | position: absolute; 62 | top: 50%; 63 | left: 50%; 64 | transform: translate(-50%, -50%); 65 | -webkit-transform: translate(-50%, -50%); 66 | background: white; 67 | border-radius: $border-radius; 68 | box-shadow: 0 1.5em 0.5em -1em rgba(0, 0, 0, 0.25); 69 | width: 70%; 70 | max-width: 375px; 71 | > div { 72 | padding:1rem; 73 | } 74 | h1 { 75 | margin-top: 0; 76 | margin-bottom: 0; 77 | font-size: 1.75rem; 78 | text-align: center; 79 | height: 2.5rem; 80 | line-height: 2.35rem; 81 | background: $color-blue; 82 | color: white; 83 | border-radius: $border-radius $border-radius 0px 0px; 84 | } 85 | h2 { 86 | margin-top: 1rem; 87 | margin-bottom: 0.15rem; 88 | font-size: 1.3rem; 89 | &:first-child { 90 | margin-top: 0; 91 | } 92 | } 93 | p:first-child { 94 | margin-top: 0; 95 | } 96 | p:last-child { 97 | margin-bottom: 0; 98 | } 99 | } 100 | } 101 | 102 | .flex { 103 | display: flex; 104 | > label, > div { 105 | flex: 1; 106 | margin-left: 0.3rem; 107 | &:first-child { 108 | margin-left: 0; 109 | } 110 | } 111 | } 112 | 113 | /** start screen: configuration **/ 114 | #options { 115 | background: rgba(0,0,0,0); 116 | .option-players label, label.option-start { 117 | display: block; 118 | margin-top: 0.5rem; 119 | color: white; 120 | background: rgba(0,150,48,0.6); 121 | text-align: center; 122 | padding: 6px; 123 | transition: background 0.5s, color 0.5s; 124 | box-sizing: border-box; 125 | border: 2px solid transparent; 126 | border-radius: $border-radius; 127 | cursor: pointer; 128 | &:hover { 129 | background: rgba(220,220,0,0.7); 130 | color: black; 131 | } 132 | &:active { 133 | border: 2px solid rgba(0,0,0,0.1); 134 | } 135 | &.option-start { 136 | background: $color-blue; 137 | color: white; 138 | margin-top: 2rem; 139 | &:hover { 140 | background: darken($color-blue, 5%); 141 | } 142 | } 143 | } 144 | .option-colors label { 145 | opacity: 0.6; 146 | margin-bottom: 0.3rem; 147 | cursor: pointer; 148 | //transition: opacity 0.5s; // commented as it creates weird jump 149 | &:hover { 150 | opacity: 0.8; 151 | } 152 | span { 153 | flex: 1; 154 | margin-left: 0.3rem; 155 | border-radius: $border-radius; 156 | display: inline-block; 157 | padding: 3px; 158 | font-size: 0.7rem; 159 | border: 2px solid transparent; 160 | &:first-child { 161 | flex: 0; 162 | margin-left: 0; 163 | background: none; 164 | font-size: 1rem; 165 | &::before { 166 | content: "\2610"; 167 | } 168 | } 169 | } 170 | &:nth-child(1) span { 171 | &:nth-child(2) { background: $color-green; } 172 | &:nth-child(3) { background: $color-blue; } 173 | &:nth-child(4) { background: $color-red; } 174 | &:nth-child(5) { background: $color-yellow; } 175 | } 176 | &:nth-child(2) span { 177 | &:nth-child(2) { background: $color-blue; } 178 | &:nth-child(3) { background: $color-red; } 179 | &:nth-child(4) { background: $color-yellow; } 180 | &:nth-child(5) { background: $color-green; } 181 | } 182 | &:nth-child(3) span { 183 | &:nth-child(2) { background: $color-red; } 184 | &:nth-child(3) { background: $color-yellow; } 185 | &:nth-child(4) { background: $color-green; } 186 | &:nth-child(5) { background: $color-blue; } 187 | } 188 | &:nth-child(4) span { 189 | &:nth-child(2) { background: $color-yellow; } 190 | &:nth-child(3) { background: $color-green; } 191 | &:nth-child(4) { background: $color-blue; } 192 | &:nth-child(5) { background: $color-red; } 193 | } 194 | } 195 | #activate-fx { 196 | cursor: pointer; 197 | &::before { 198 | content: "\2610\00a0"; 199 | } 200 | } 201 | } 202 | 203 | // hide the game settings when Start is pressed 204 | #game-time:checked ~ #options, 205 | [name=show-instructions]:checked ~ #options { display: none; } 206 | #play-fx:checked ~ #options #activate-fx::before { content: "\2612\00a0" } 207 | 208 | // control active # of players and colors 209 | @for $i from 1 to 5 { 210 | #players#{$i}:checked { 211 | ~ #options { 212 | label[for=players#{$i}] { 213 | background: rgba(0,150,48,1); 214 | border: 2px solid rgba(0,0,0,0.1); 215 | color: white; 216 | } 217 | @for $j from $i+1 to 5 { 218 | label[for^=colors] span:nth-child(#{$j+1}){ 219 | display: none; 220 | } 221 | } 222 | } 223 | } 224 | #colors#{$i}:checked { 225 | ~ #options { 226 | label[for=colors#{$i}] { 227 | opacity: 1; 228 | border: 0; 229 | span { 230 | border: 2px solid rgba(0,0,0,0.1); 231 | &:first-child { 232 | border: 2px solid transparent; 233 | &::before { 234 | content:"\2612"; 235 | } 236 | } 237 | } 238 | } 239 | } 240 | } 241 | } 242 | 243 | // assign colors to the game pieces according to the color scheme selected 244 | // TODO: this probably can be reduced with SASS as well as the code above 245 | #colors1:checked ~ #game #piece-player-1 g { fill: $color-green; } 246 | #colors1:checked ~ #game #piece-player-2 g { fill: $color-blue; } 247 | #colors1:checked ~ #game #piece-player-3 g { fill: $color-red; } 248 | #colors1:checked ~ #game #piece-player-4 g { fill: $color-yellow; } 249 | #colors2:checked ~ #game #piece-player-1 g { fill: $color-blue; } 250 | #colors2:checked ~ #game #piece-player-2 g { fill: $color-red; } 251 | #colors2:checked ~ #game #piece-player-3 g { fill: $color-yellow; } 252 | #colors2:checked ~ #game #piece-player-4 g { fill: $color-green; } 253 | #colors3:checked ~ #game #piece-player-1 g { fill: $color-red; } 254 | #colors3:checked ~ #game #piece-player-2 g { fill: $color-yellow; } 255 | #colors3:checked ~ #game #piece-player-3 g { fill: $color-green; } 256 | #colors3:checked ~ #game #piece-player-4 g { fill: $color-blue; } 257 | #colors4:checked ~ #game #piece-player-1 g { fill: $color-yellow; } 258 | #colors4:checked ~ #game #piece-player-2 g { fill: $color-green; } 259 | #colors4:checked ~ #game #piece-player-3 g { fill: $color-blue; } 260 | #colors4:checked ~ #game #piece-player-4 g { fill: $color-red; } 261 | 262 | /** info pop-up **/ 263 | #info { 264 | display: none; 265 | .box { 266 | width: 75%; 267 | max-width: none; 268 | p:last-child { 269 | text-align: right; 270 | } 271 | #close-button { 272 | display: inline-block; 273 | background: $color-green; 274 | border-radius: $border-radius; 275 | color: white; 276 | padding: 6px 1rem; 277 | cursor: pointer; 278 | transition: background 0.5s; 279 | &:hover { 280 | background: darken($color-green, 5%); 281 | } 282 | } 283 | } 284 | } 285 | #show-info:checked { 286 | ~ #info { display: block; } 287 | } 288 | 289 | 290 | /** Instructions **/ 291 | #instructions { 292 | position: fixed; 293 | z-index: 9999; 294 | background-color: rgba(0,0,0,0.01); 295 | top: 0; 296 | left: 0; 297 | width: 100%; 298 | height: 100%; 299 | display: none; 300 | #go-to-game { 301 | position: absolute; 302 | bottom: 1rem; 303 | right: 1rem; 304 | font-size: 1rem; 305 | z-index: 3; 306 | color: white; 307 | cursor: pointer; 308 | } 309 | #step { 310 | display: none; 311 | position: absolute; 312 | box-shadow: 0 0 50px calc(200vmax) rgba(0,0,0,0.85), inset 0 0 4px 0 rgba(0,0,0,1); 313 | transform: translate(-1rem, -1rem); 314 | border-radius: 100%; 315 | z-index: 1; 316 | top: 0; 317 | left: 1rem; 318 | width: calc(100px + 2rem); 319 | height: calc(6rem); 320 | transition: all 0.75s; 321 | div { 322 | position: absolute; 323 | bottom: -0.5rem; 324 | line-height: 1.1rem; 325 | font-size: 1rem; 326 | color: rgba(255,255,255,0.8); 327 | left: 1rem; 328 | width: 80vmin; 329 | transform: translate(0, 100%); 330 | -webkit-transform: translate(0, 100%); 331 | label { 332 | display: none; 333 | font-weight: bold; 334 | cursor: pointer; 335 | text-decoration: underline; 336 | &:hover { 337 | text-decoration: none; 338 | } 339 | &:not([for=game-time])::before { 340 | content: "Next \203A" 341 | } 342 | } 343 | } 344 | } 345 | } 346 | 347 | [name=show-instructions] { 348 | &:checked ~ { 349 | #instructions, #game, #action, #instructions #step { 350 | display: block; 351 | } 352 | } 353 | } 354 | 355 | @for $i from 1 to 5 { 356 | #show-instructions-#{$i}:checked ~ #instructions label[for=show-instructions-#{$i+1}] { 357 | display: inline-block; 358 | } 359 | } 360 | #show-instructions-5:checked ~ #instructions label[for=game-time] { 361 | display: inline-block; 362 | } 363 | 364 | #show-instructions-1:checked ~ #instructions #step { 365 | top: 0; 366 | left: 1rem; 367 | width: calc(100px + 2rem); 368 | height: calc(6rem); 369 | div::before { 370 | content: "This area indicates whose turn it is at any given moment."; 371 | } 372 | } 373 | #show-instructions-2:checked ~ #instructions #step { 374 | top:6rem; 375 | left: 1rem; 376 | width: 5rem; 377 | height: 5rem; 378 | div::before { 379 | content: "The active player clicks here to roll the dice."; 380 | } 381 | } 382 | #show-instructions-3:checked ~ #instructions #step { 383 | top: 50%; 384 | left: 50%; 385 | width: 30vw; 386 | height: 30vw; 387 | max-width: 500px; 388 | max-height: 500px; 389 | transform: translate(-50%, -50%); 390 | -webkit-transform: translate(-50%, -50%); 391 | div { 392 | transform: translate(-20vmin, 100%); 393 | -webkit-transform: translate(-20vmin, 100%); 394 | left: 0; 395 | &::before { 396 | content: "The player's piece will advance on the board once the dice is rolled. If the piece falls on a tile with a snake's head, it will fall down to the snake's tail. If it falls on a ladder's bottom, it will go up to the top."; 397 | } 398 | } 399 | } 400 | #show-instructions-4:checked ~ #instructions #step { 401 | top: 0; 402 | left: calc(100px + 2rem); 403 | width: calc(100px + 2rem); 404 | height: calc(6rem); 405 | div::before { 406 | content: "After that, click on the button to go to the next player's turn."; 407 | } 408 | } 409 | #show-instructions-5:checked ~ #instructions #step { 410 | top: 0; 411 | left: calc(100vw - 100px - 1rem); 412 | width: calc(100px + 2rem); 413 | height: calc(6rem); 414 | div { 415 | left: auto; 416 | right: 0rem; 417 | &::before { 418 | content: "If you want more information during the game, click on this button." 419 | } 420 | } 421 | } 422 | 423 | #game-time:checked ~ #instructions { 424 | display: none; 425 | } 426 | 427 | /** game screen: board, dice, messages **/ 428 | .dice { 429 | position: absolute; 430 | display: none; 431 | visibility: visible; 432 | top: 6rem; 433 | left: 1rem; 434 | width: 3rem; 435 | height: 3rem; 436 | background: white; 437 | border-radius: $border-radius; 438 | box-shadow: 0 1.5em 0.5em -1em rgba(0, 0, 0, 0.042); 439 | z-index:1; 440 | &::before { 441 | content: ""; 442 | display: block; 443 | height: 100%; 444 | width: 100%; 445 | font-size: 0.8rem; 446 | box-sizing: border-box; 447 | cursor: pointer; 448 | padding-top: 0.5rem; 449 | background-repeat: no-repeat; 450 | background-position: center center; 451 | } 452 | } 453 | 454 | 455 | @for $i from 1 to 5 { 456 | #turn#{$i}:checked ~ .game-time:checked ~ [name=cb-player#{$i}]:checked + label + input + label, 457 | #turn#{$i}:checked ~ .game-time:checked ~ [name=cb-player#{$i}]:checked + label + input + label + input + label, 458 | #turn#{$i}:checked ~ .game-time:checked ~ [name=cb-player#{$i}]:checked + label + input + label + input + label + input + label, 459 | #turn#{$i}:checked ~ .game-time:checked ~ [name=cb-player#{$i}]:checked + label + input + label + input + label + input + label + input + label, 460 | #turn#{$i}:checked ~ .game-time:checked ~ [name=cb-player#{$i}]:checked + label + input + label + input + label + input + label + input + label + input + label, 461 | #turn#{$i}:checked ~ .game-time:checked ~ [name=cb-player#{$i}]:checked + label + input + label + input + label + input + label + input + label + input + label + input + label { 462 | display: block; 463 | left: 1rem; 464 | text-align: center; 465 | animation: changeOrder $animation-time infinite; 466 | -webkit-animation: changeOrder $animation-time infinite; 467 | } 468 | } 469 | 470 | @for $i from 1 to 5 { 471 | #turn#{$i}:checked ~ [name=cb-player#{$i}]:checked + label + input + label { animation-delay: 0s !important; } 472 | #turn#{$i}:checked ~ [name=cb-player#{$i}]:checked + label + input + label + input + label { animation-delay: -$animation-time/6 !important; } 473 | #turn#{$i}:checked ~ [name=cb-player#{$i}]:checked + label + input + label + input + label + input + label { animation-delay: -2*$animation-time/6 !important; } 474 | #turn#{$i}:checked ~ [name=cb-player#{$i}]:checked + label + input + label + input + label + input + label + input + label { animation-delay: -3*$animation-time/6 !important; } 475 | #turn#{$i}:checked ~ [name=cb-player#{$i}]:checked + label + input + label + input + label + input + label + input + label + input + label { animation-delay: -4*$animation-time/6 !important; } 476 | #turn#{$i}:checked ~ [name=cb-player#{$i}]:checked + label + input + label + input + label + input + label + input + label + input + label + input + label { animation-delay: -5*$animation-time/6 !important; } 477 | } 478 | 479 | @for $i from 1 to 5 { 480 | 481 | #turn#{$i}:checked ~ [name=cb-player#{$i}]:checked + label + input + label::before { background-image: 482 | url("data:image/svg+xml;utf8,"); 483 | } 484 | #turn#{$i}:checked ~ [name=cb-player#{$i}]:checked + label + input + label + input + label::before { background-image: 485 | url("data:image/svg+xml;utf8,"); } 486 | #turn#{$i}:checked ~ [name=cb-player#{$i}]:checked + label + input + label + input + label + input + label::before { background-image: 487 | url("data:image/svg+xml;utf8,"); } 488 | #turn#{$i}:checked ~ [name=cb-player#{$i}]:checked + label + input + label + input + label + input + label + input + label::before { background-image: 489 | url("data:image/svg+xml;utf8,"); } 490 | #turn#{$i}:checked ~ [name=cb-player#{$i}]:checked + label + input + label + input + label + input + label + input + label + input + label::before { background-image: 491 | url("data:image/svg+xml;utf8,"); } 492 | #turn#{$i}:checked ~ [name=cb-player#{$i}]:checked + label + input + label + input + label + input + label + input + label + input + label + input + label::before { background-image: 493 | url("data:image/svg+xml;utf8,"); } 494 | 495 | } 496 | 497 | 498 | // label:active adapted from solution by Temani Afif. Ref: https://stackoverflow.com/a/51451218/3695983 499 | label.dice:active { 500 | position:static !important; 501 | margin-left: 50px !important; 502 | background: none !important; 503 | font-size: 0; 504 | &::before { 505 | content:""; 506 | position:absolute; 507 | top:0; 508 | right:-50px; 509 | left:0; 510 | bottom:0; 511 | z-index:200; 512 | } 513 | } 514 | 515 | #game { 516 | display: none; 517 | .tab { 518 | position: absolute; 519 | top: 0; 520 | background: white; 521 | border-radius: 0 0 3px 3px; 522 | box-shadow: 0 1.5em 0.5em -1em rgba(0, 0, 0, 0.25); 523 | z-index: 9; 524 | float:left; 525 | margin-left: 1rem; 526 | &::before { 527 | text-align: center; 528 | display: block; 529 | padding: 6px 16px; 530 | color: white; 531 | background: $color-blue; 532 | height: 0.7rem; 533 | line-height: 0.7rem; 534 | font-size: 0.75rem; 535 | font-weight: bold; 536 | white-space: nowrap; 537 | text-transform: uppercase; 538 | font-size: 0.95rem; 539 | } 540 | &::after, &#player-info span { 541 | display: block; 542 | text-align: center; 543 | padding: 6px 16px; 544 | height: 2rem; 545 | line-height: 1.9rem; 546 | white-space: nowrap; 547 | } 548 | &#player-info { 549 | min-width: 100px; 550 | &::before { content: "Turn"; } 551 | span { 552 | font-weight: bolder; 553 | &::before { 554 | content: "\25CF\00A0"; 555 | } 556 | &::after { 557 | content: " 1"; 558 | font-weight: bolder; 559 | } 560 | } 561 | } 562 | &#dice { 563 | cursor: pointer; 564 | &::before { content: "Dice"; } 565 | &::after { 566 | content: "Roll"; 567 | } 568 | } 569 | &#more-info { 570 | cursor: pointer; 571 | left: auto; 572 | right: 1rem; 573 | width: 100px; 574 | z-index: 1; 575 | &::before { content: "About"; } 576 | &::after { content: "More info" } 577 | } 578 | &#action { 579 | cursor: pointer; 580 | left: calc(100px + 1rem); 581 | width: 100px; 582 | &::before { content: "Action"; } 583 | &::after { content: "Next player" } 584 | label { 585 | position: absolute; 586 | top: 0; 587 | left: 0; 588 | bottom: 0; 589 | right: 0; 590 | cursor: pointer; 591 | } 592 | } 593 | } 594 | #board { 595 | width: 70%; 596 | max-width: 500px; 597 | border: 3px solid $color-blue; 598 | border-radius: $border-radius * 2; 599 | position: absolute; 600 | top: 50%; 601 | left: 50%; 602 | transform: translate(-50%, -50%); 603 | -webkit-transform: translate(-50%, -50%); 604 | background: white; 605 | margin-top: 2.5rem; 606 | box-shadow: 0 1.5em 0.5em -1em rgba(0, 0, 0, 0.25); 607 | .tile { 608 | float: left; 609 | width: 10%; 610 | position: relative; 611 | height: 0; 612 | padding-top: 10%; 613 | box-sizing: border-box; 614 | border-left: 1px solid rgba(0,0,0,0.1); 615 | border-right: 1px solid rgba(0,0,0,0.1); 616 | border-top: 1px solid rgba(0,0,0,1); 617 | border-bottom: 1px solid rgba(0,0,0,1); 618 | background: lighten(#9ad0ac,15%); 619 | &::before { 620 | content: attr(data-index); 621 | position: absolute; 622 | top: 0rem; 623 | left: 0.15rem; 624 | font-size: 0.66rem; 625 | } 626 | &.tile6, &.tile12, &.tile18, &.tile22, &.tile23, &.tile28, &.tile30, &.tile34, &.tile36, &.tile39, &.tile55, &.tile79 { 627 | &::before { 628 | left: auto; 629 | right: 0.15rem; 630 | } 631 | } 632 | &.tile52::before { 633 | top:auto; 634 | bottom:0rem; 635 | } 636 | &.tile76::before { 637 | left: 0.6rem; 638 | } 639 | } 640 | @for $i from 1 to 11 { 641 | .tile#{$i} { border-bottom: 1px solid transparent; } 642 | .tile#{$i+90} { border-top: 1px solid transparent; } 643 | .tile#{$i*10} { border-top: 1px solid rgba(0,0,0,0.1); } 644 | .tile#{$i*10+1} { border-bottom: 1px solid rgba(0,0,0,0.1); } 645 | } 646 | .tile1, .tile15, .tile24, .tile38, .tile49, .tile55, .tile65, .tile76, .tile86, .tile94 { 647 | background: lighten(#f198b4, 15%); 648 | } 649 | 650 | .tile2, .tile16, .tile23, .tile35, .tile42, .tile56, .tile64, .tile79, .tile87, .tile93 { 651 | background: lighten(#C3D9FF, 8%); 652 | } 653 | 654 | .tile3, .tile18, .tile26, .tile33, .tile41, .tile58, .tile68, .tile78, .tile81, .tile96 { 655 | background: lighten(#f89448, 30%); 656 | } 657 | 658 | .tile4, .tile11, .tile29, .tile40, .tile47, .tile51, .tile61, .tile77, .tile88, .tile99 { 659 | background: lighten(#c297c5, 23%); 660 | } 661 | 662 | .tile5, .tile19, .tile27, .tile32, .tile44, .tile59, .tile69, .tile80, .tile89, .tile97 { 663 | background: lighten(#68caef, 18%); 664 | } 665 | 666 | .tile6, .tile20, .tile28, .tile31, .tile45, .tile60, .tile70, .tile75, .tile85, .tile98 { 667 | background: lighten(#fef471, 21%); 668 | } 669 | 670 | .tile7, .tile13, .tile21, .tile36, .tile50, .tile53, .tile63, .tile74, .tile84, .tile91 { 671 | background: lighten(#b9e6fd, 5%); 672 | } 673 | 674 | .tile8, .tile12, .tile30, .tile39, .tile46, .tile52, .tile62, .tile73, .tile90, .tile100 { 675 | background: lighten(#e5b75f,19%); 676 | } 677 | 678 | .tile9, .tile14, .tile22, .tile37, .tile48, .tile54, .tile66, .tile72, .tile83, .tile96 { 679 | background: lighten(#f37859,20%); 680 | } 681 | } 682 | } 683 | 684 | #action label { display: none; } 685 | #players1:checked ~ #turn1:checked ~ #game-time:checked ~ #game #action { display:none; } 686 | #players1:checked ~ #turn1:checked ~ #game #turn-changer1 { display: block; } 687 | #players2:checked ~ #turn1:checked ~ #game #turn-changer2 { display: block; } 688 | #players3:checked ~ #turn1:checked ~ #game #turn-changer2 { display: block; } 689 | #players4:checked ~ #turn1:checked ~ #game #turn-changer2 { display: block; } 690 | #players2:checked ~ #turn2:checked ~ #game #turn-changer1 { display: block; } 691 | #players3:checked ~ #turn2:checked ~ #game #turn-changer3 { display: block; } 692 | #players4:checked ~ #turn2:checked ~ #game #turn-changer3 { display: block; } 693 | #players3:checked ~ #turn3:checked ~ #game #turn-changer1 { display: block; } 694 | #players4:checked ~ #turn3:checked ~ #game #turn-changer4 { display: block; } 695 | #players4:checked ~ #turn4:checked ~ #game #turn-changer1 { display: block; } 696 | 697 | // assign colors to the game pieces according to the color scheme selected 698 | // TODO: this probably can be reduced with SASS as well as the code above for piece colors 699 | #turn1:checked ~ #colors1:checked ~ #game #player-info span::before { color: $color-green; } 700 | #turn2:checked ~ #colors1:checked ~ #game #player-info span::before { color: $color-blue; } 701 | #turn3:checked ~ #colors1:checked ~ #game #player-info span::before { color: $color-red; } 702 | #turn4:checked ~ #colors1:checked ~ #game #player-info span::before { color: $color-yellow; } 703 | #turn1:checked ~ #colors2:checked ~ #game #player-info span::before { color: $color-blue; } 704 | #turn2:checked ~ #colors2:checked ~ #game #player-info span::before { color: $color-red; } 705 | #turn3:checked ~ #colors2:checked ~ #game #player-info span::before { color: $color-yellow; } 706 | #turn4:checked ~ #colors2:checked ~ #game #player-info span::before { color: $color-green; } 707 | #turn1:checked ~ #colors3:checked ~ #game #player-info span::before { color: $color-red; } 708 | #turn2:checked ~ #colors3:checked ~ #game #player-info span::before { color: $color-yellow; } 709 | #turn3:checked ~ #colors3:checked ~ #game #player-info span::before { color: $color-green; } 710 | #turn4:checked ~ #colors3:checked ~ #game #player-info span::before { color: $color-blue; } 711 | #turn1:checked ~ #colors4:checked ~ #game #player-info span::before { color: $color-yellow; } 712 | #turn2:checked ~ #colors4:checked ~ #game #player-info span::before { color: $color-green; } 713 | #turn3:checked ~ #colors4:checked ~ #game #player-info span::before { color: $color-blue; } 714 | #turn4:checked ~ #colors4:checked ~ #game #player-info span::before { color: $color-red; } 715 | 716 | @for $i from 1 to 5 { 717 | #turn#{$i}:checked ~ #game .tab#player-info span::after{ 718 | content: " #{$i}"; 719 | } 720 | } 721 | 722 | // show the game board when Start is pressed 723 | #game-time:checked ~ #game { 724 | display: block; 725 | } 726 | 727 | svg { 728 | &#snakeladders{ 729 | position: absolute; 730 | top: 0; 731 | left: 0; 732 | width: 100%; 733 | height: 100%; 734 | g { 735 | &.ladders-big { 736 | stroke: #8B4513; 737 | stroke-width: 4; 738 | stroke-linecap: round; 739 | } 740 | &.ladders-small { 741 | stroke: #8B4513; 742 | stroke-width: 2.5; 743 | stroke-linecap: round; 744 | } 745 | } 746 | } 747 | .snake { 748 | stroke: none; 749 | } 750 | .eye { 751 | fill: black; 752 | stroke: white; 753 | stroke-width: 6; 754 | } 755 | &.piece { 756 | position: absolute; 757 | width: 8%; 758 | bottom: 0; 759 | left: 0; 760 | transform: translate(-2%, -2%); 761 | -webkit-transform: translate(-2%, -2%); 762 | transition: bottom 0.5s, left 0.5s; 763 | g { 764 | fill: $color-red; 765 | stroke-width: 4; 766 | stroke: rgba(0,0,0,0.3); 767 | } 768 | &#piece-player-1 { 769 | transform: translate(-5%, -3%); 770 | -webkit-transform: translate(-5%, -3%); 771 | z-index:44; 772 | g { 773 | fill: $color-red; 774 | } 775 | } 776 | &#piece-player-2 { 777 | transform: translate(40%, -40%); 778 | -webkit-transform: translate(40%, -40%); 779 | z-index: 41; 780 | g { 781 | fill: $color-blue; 782 | } 783 | } 784 | &#piece-player-3 { 785 | transform: translate(42%, -7%); 786 | -webkit-transform: translate(42%, -7%); 787 | z-index: 43; 788 | g { 789 | fill: $color-green; 790 | } 791 | } 792 | &#piece-player-4 { 793 | transform: translate(-3%, -41%); 794 | -webkit-transform: translate(-3%, -41%); 795 | z-index: 42; 796 | g { 797 | fill: $color-yellow; 798 | } 799 | } 800 | } 801 | } 802 | 803 | // generate the different positions on each tile 804 | $positions: 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 90, 80, 70, 60, 50, 40, 30, 20, 10, 0; 805 | @for $i from 1 through 4 { 806 | #cb-pl#{$i+"-0"}:checked ~ #game #piece-player-#{$i} { 807 | left: -10%; 808 | bottom: 0; 809 | } 810 | @for $j from 1 to 101 { 811 | #cb-pl#{$i}-#{$j}:checked { 812 | ~ #game #piece-player-#{$i} { 813 | left: nth($positions, (($j - 1) % 20) + 1) * 1%; 814 | bottom: nth($positions, floor(($j - 1) / 10) + 1) * 1%; 815 | } 816 | } 817 | } 818 | @for $j from 101 to 107 { 819 | #cb-pl#{$i}-#{$j}:checked { 820 | ~ #game #piece-player-#{$i} { 821 | left: 0; 822 | bottom: 90%; 823 | } 824 | } 825 | } 826 | } 827 | 828 | // hide the players that are not active 829 | @for $i from 1 to 5 { 830 | @for $j from $i+1 to 5 { 831 | #players#{$i}:checked ~ #game #piece-player-#{$j} { 832 | display: none !important; 833 | } 834 | } 835 | } 836 | 837 | 838 | // all these popups have a similar code below that could be reduced with a mixin (TODO) 839 | #snake-popup, #ladder-popup, #home-popup, #congratulations { 840 | opacity: 0; 841 | z-index: -1; 842 | transition: opacity 0s; 843 | label { 844 | display: none; 845 | } 846 | svg { 847 | height:100%; 848 | position:absolute; 849 | right:0; 850 | top:0; 851 | transform:translate(80%, 0); 852 | } 853 | } 854 | 855 | // when we fall in a ladder, show the ladder popup 856 | @each $l in $ladders { 857 | .cb[id$='-#{$l}'] { 858 | &:checked { 859 | ~ .dice { 860 | display: none !important; 861 | } 862 | ~ #ladder-popup { 863 | opacity: 1; 864 | z-index: 99999; 865 | transition: opacity 0.15s; 866 | transition-delay: 0.5s; 867 | -webkit-transition-delay: 0.5s; 868 | label { 869 | background: $color-green; 870 | border-radius: $border-radius; 871 | color: white; 872 | padding: 6px 1rem; 873 | cursor: pointer; 874 | transition: background 0.5s; 875 | margin-top: 1rem; 876 | &:hover { 877 | background: darken($color-green, 5%); 878 | } 879 | } 880 | } 881 | } 882 | } 883 | } 884 | 885 | // when we fall in a ladder, show the ladder popup 886 | @each $s in $snakes { 887 | .cb[id$='-#{$s}'] { 888 | &:checked { 889 | ~ .dice { 890 | display: none !important; 891 | } 892 | ~ #snake-popup { 893 | opacity: 1; 894 | z-index: 99999; 895 | transition: opacity 0.15s; 896 | transition-delay: 0.5s; 897 | -webkit-transition-delay: 0.5s; 898 | label { 899 | background: $color-green; 900 | border-radius: $border-radius; 901 | color: white; 902 | padding: 6px 1rem; 903 | cursor: pointer; 904 | transition: background 0.5s; 905 | margin-top: 1rem; 906 | &:hover { 907 | background: darken($color-green, 5%); 908 | } 909 | } 910 | } 911 | } 912 | } 913 | } 914 | 915 | // when we fall in a ladder, show the ladder popup 916 | @for $s from 101 to 107 { 917 | .cb[id$='-#{$s}'] { 918 | &:checked { 919 | ~ .dice { 920 | display: none !important; 921 | } 922 | ~ #home-popup { 923 | opacity: 1; 924 | z-index: 99999; 925 | transition: opacity 0.15s; 926 | transition-delay: 0.5s; 927 | -webkit-transition-delay: 0.5s; 928 | label { 929 | background: $color-green; 930 | border-radius: $border-radius; 931 | color: white; 932 | padding: 6px 1rem; 933 | cursor: pointer; 934 | transition: background 0.5s; 935 | margin-top: 1rem; 936 | &:hover { 937 | background: darken($color-green, 5%); 938 | } 939 | } 940 | } 941 | } 942 | } 943 | } 944 | 945 | // show the right button in the ladder/snake popup 946 | @for $i from 1 to 5 { 947 | @each $l in $ladders { 948 | #cb-pl#{$i}-#{$l}:checked ~ #ladder-popup #ladder-pl#{$i}-#{$l} { display: inline-block; } 949 | } 950 | @each $s in $snakes { 951 | #cb-pl#{$i}-#{$s}:checked ~ #snake-popup #snake-pl#{$i}-#{$s} { display: inline-block; } 952 | } 953 | @for $j from 101 to 107 { 954 | #cb-pl#{$i}-#{$j}:checked ~ #home-popup #home-pl#{$i}-#{$j} { display: inline-block; } 955 | } 956 | } 957 | 958 | .cb[id$='-100']:checked { 959 | ~ #congratulations { 960 | opacity: 1; 961 | z-index: 99999; 962 | transition: opacity 0.15s; 963 | transition-delay: 0.5s; 964 | -webkit-transition-delay: 0.5s; 965 | } 966 | } 967 | 968 | @media (max-width: 400px) { 969 | #game { 970 | #board .tile::before { 971 | font-size: 0.4rem; 972 | } 973 | .tab { 974 | &::before { 975 | font-size: 0.65rem; 976 | } 977 | &::after, &#player-info span { 978 | font-size: 0.9rem; 979 | height: 1.5rem; 980 | line-height: 1.4rem; 981 | } 982 | } 983 | } 984 | } 985 | -------------------------------------------------------------------------------- /animated-dice/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Snakes and Ladders 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 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | 738 | 739 | 740 | 741 | 742 | 743 | 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 | 775 | 776 | 777 | 778 | 779 | 780 | 781 | 782 | 783 | 784 | 785 | 786 | 787 | 788 | 789 | 790 | 791 | 792 | 793 | 794 | 795 | 796 | 797 | 798 | 799 | 800 | 801 | 802 | 803 | 804 | 805 | 806 | 807 | 808 | 809 | 810 | 811 | 812 | 813 | 814 | 815 | 816 | 817 | 818 | 819 | 820 | 821 | 822 | 823 | 824 | 825 | 826 | 827 | 828 | 829 | 830 | 831 | 832 | 833 | 834 | 835 | 836 | 837 | 838 | 839 | 840 | 841 | 842 | 843 | 844 | 845 | 846 | 847 | 848 | 849 | 850 | 851 | 852 | 853 | 854 | 855 | 856 | 857 | 858 | 859 | 860 | 861 | 862 | 863 | 864 | 865 | 866 | 867 | 868 | 869 | 870 | 871 | 872 | 873 | 874 | 875 | 876 | 877 | 878 | 879 | 880 | 881 | 882 | 883 | 884 | 885 | 886 | 887 | 888 | 889 | 890 | 891 | 892 | 893 | 894 | 895 | 896 | 897 |
898 |
899 |

Game Settings

900 |
901 |

Number of players

902 |
903 | 904 | 905 | 906 | 907 |
908 |

Piece colors

909 |
910 | 911 | 912 | 913 | 914 |
915 |

Sound options

916 | 917 |
918 | 919 |
920 |
921 |
922 |
923 | 924 |
925 |
926 |

About

927 |
928 |

Snakes and Ladders is an ancient Indian board game regarded today as a worldwide classic. A number of 'ladders' and 'snakes' are pictured on the board, each connecting two specific board squares. The object of the game is to navigate one's game piece, according to die rolls, from the start (bottom square) to the finish (top square), helped or hindered by ladders and snakes respectively.[1]

929 |

This version was developed by Alvaro Montoro using HTML and CSS, to practice Pug and Sass, and without any JavaScript line of code.

930 |

The idea behind this project is that many classic board games are actually Finite-State Machines (FSM), so they can be recreated simulating the states of the automaton with radio buttons, labels, and some CSS styling.

931 |

932 | 933 |

934 |
935 |
936 |
937 | 938 |
939 | 940 |
941 |
942 | 943 | 944 | 945 | 946 | 947 |
948 |
949 |
950 | 951 |
952 |
Player
953 | 954 |
955 | 956 | 957 | 958 | 959 |
960 | 961 |
962 |
963 | 964 |
965 | 966 |
967 | 968 |
969 |
970 |
971 |
972 |
973 |
974 |
975 |
976 |
977 |
978 |
979 |
980 |
981 |
982 |
983 |
984 |
985 |
986 |
987 |
988 |
989 |
990 |
991 |
992 |
993 |
994 |
995 |
996 |
997 |
998 |
999 |
1000 |
1001 |
1002 |
1003 |
1004 |
1005 |
1006 |
1007 |
1008 |
1009 |
1010 |
1011 |
1012 |
1013 |
1014 |
1015 |
1016 |
1017 |
1018 |
1019 |
1020 |
1021 |
1022 |
1023 |
1024 |
1025 |
1026 |
1027 |
1028 |
1029 |
1030 |
1031 |
1032 |
1033 |
1034 |
1035 |
1036 |
1037 |
1038 |
1039 |
1040 |
1041 |
1042 |
1043 |
1044 |
1045 |
1046 |
1047 |
1048 |
1049 |
1050 |
1051 |
1052 |
1053 |
1054 |
1055 |
1056 |
1057 |
1058 |
1059 |
1060 |
1061 |
1062 |
1063 |
1064 |
1065 |
1066 |
1067 |
1068 |
1069 | 1070 | 1071 | 1072 | 1073 | 1074 | 1075 | 1076 | 1077 | 1078 | 1079 | 1080 | 1081 | 1082 | 1083 | 1084 | 1085 | 1086 | 1087 | 1088 | 1089 | 1090 | 1091 | 1092 | 1093 | 1094 | 1095 | 1096 | 1097 | 1098 | 1099 | 1100 | 1101 | 1102 | 1103 | 1104 | 1105 | 1106 | 1107 | 1108 | 1109 | 1110 | 1111 | 1112 | 1113 | 1114 | 1115 | 1116 | 1117 | 1118 | 1119 | 1120 | 1121 | 1122 | 1123 | 1124 | 1125 | 1126 |
1127 |
1128 |
1129 |
1130 |

Snake!!!

1131 |
Oh no! The snake bit you! You'll have to move your piece to the snake's tail. 1132 |
1133 | 1134 | 1135 | 1136 | 1137 | 1138 | 1139 | 1140 | 1141 | 1142 | 1143 | 1144 | 1145 | 1146 | 1147 | 1148 | 1149 | 1150 | 1151 | 1152 | 1153 | 1154 | 1155 | 1156 | 1157 | 1158 | 1159 | 1160 | 1161 | 1162 | 1163 | 1164 | 1165 | 1166 | 1167 | 1168 | 1169 | 1170 | 1171 | 1172 | 1173 |
1174 |
1175 | 1176 | 1177 | 1178 | 1179 | 1180 |
1181 |
1182 |
1183 |
1184 |

Ladder!

1185 |
Yay! You found a ladder! You can climb up now. 1186 |
1187 | 1188 | 1189 | 1190 | 1191 | 1192 | 1193 | 1194 | 1195 | 1196 | 1197 | 1198 | 1199 | 1200 | 1201 | 1202 | 1203 | 1204 | 1205 | 1206 | 1207 | 1208 | 1209 | 1210 | 1211 | 1212 | 1213 | 1214 | 1215 | 1216 | 1217 | 1218 | 1219 | 1220 | 1221 | 1222 | 1223 | 1224 | 1225 | 1226 | 1227 | 1228 | 1229 | 1230 | 1231 |
1232 |
1233 | 1234 | 1235 | 1236 | 1237 | 1238 |
1239 |
1240 |
1241 |
1242 |

Home

1243 |
You made it home, but you didn't do it in an exact jump so you will bounce back the remaining tiles. 1244 |
1245 | 1246 | 1247 | 1248 | 1249 | 1250 | 1251 | 1252 | 1253 | 1254 | 1255 | 1256 | 1257 | 1258 | 1259 | 1260 | 1261 | 1262 | 1263 | 1264 | 1265 | 1266 | 1267 | 1268 | 1269 |
1270 |
1271 | 1272 | 1273 | 1274 | 1275 |
1276 |
1277 |
1278 |
1279 |

Congratulations!

1280 |
You made it home safely! You won!
1281 |
Reload the page to play again.
1282 | 1283 | 1284 | 1285 | 1286 |
1287 |
1288 |
1289 | 1290 |
1291 |
1292 | 1293 | 1294 |
1295 | 1296 | 1297 | 1298 | --------------------------------------------------------------------------------