-
-
Save LeetCodes/3b058d640562b074948a45a5c8cca080 to your computer and use it in GitHub Desktop.
Pure CSS Game Stacker
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@import url('https://fonts.googleapis.com/css?family=VT323'); | |
$block-gap: 8px; | |
$block-size: 25px; | |
$blocks-per-row: 7; | |
$blocks-color: #233f5a; | |
$blocks-active: #fabc7f; | |
$blocks-bg: #172031; | |
$speed: 2.5s; | |
* { | |
box-sizing: border-box; | |
} | |
html, | |
body { | |
height: 100%; | |
} | |
body { | |
align-items: center; | |
background: $blocks-bg; | |
background-attachment: fixed; | |
display: flex; | |
flex-direction: column; | |
font-family: 'VT323', monospace; | |
justify-content: center; | |
} | |
h1 { | |
color: white; | |
font-size: 30px !important; | |
letter-spacing: 0.05em; | |
margin: 20px auto 0 !important; | |
text-align: center; | |
text-transform: uppercase; | |
} | |
form { | |
border: 6px solid #2f5d83; | |
padding: 25px 20px 80px; | |
position: relative; | |
} | |
input[type=radio] { | |
appearance: none; | |
font-size: 9px; | |
left: -10px; | |
opacity: 0.01; | |
position: fixed; | |
top: -10px; | |
} | |
@keyframes blockExplode { | |
0% { | |
background: $blocks-active; | |
box-shadow: none; | |
} | |
40% { | |
box-shadow: 0 0 5px lighten($blocks-active, 20%); | |
} | |
50% { | |
background: white; | |
box-shadow: 0 0 30px lighten($blocks-active, 20%); | |
} | |
60% { | |
background: $blocks-color; | |
box-shadow: none; | |
} | |
100% { | |
background: $blocks-color; | |
box-shadow: none; | |
} | |
} | |
@mixin blockInactive () { | |
animation-name: blockExplode; | |
animation-duration: .75s; | |
animation-iteration-count: 1; | |
animation-fill-mode: forwards; | |
} | |
@mixin blockActive () { | |
background: $blocks-active; | |
} | |
.bs { | |
background: $blocks-bg; | |
display: flex; | |
flex-direction: column-reverse; | |
height: ($block-size + $block-gap) * 12; | |
position: relative; | |
width: ($block-size + $block-gap) * $blocks-per-row; | |
.r { | |
display: flex; | |
margin: 0; | |
width: 100%; | |
&:nth-of-type(8) .b { | |
background: lighten($blocks-color, 8%); | |
} | |
&:nth-of-type(12) .b { | |
background: lighten($blocks-color, 15%); | |
} | |
} | |
.b { | |
background: $blocks-color; | |
height: $block-size; | |
margin: 0 $block-gap / 2 $block-gap; | |
width: $block-size; | |
} | |
} | |
/** | |
* Global Functions | |
**/ | |
@function rowName ($number) { | |
@if $number == 1 { | |
@return 'on'; | |
} | |
@elseif $number == 2 { | |
@return 'tw'; | |
} | |
@elseif $number == 3 { | |
@return 'thr'; | |
} | |
@elseif $number == 4 { | |
@return 'fr'; | |
} | |
@elseif $number == 5 { | |
@return 'fv'; | |
} | |
@elseif $number == 6 { | |
@return 'sx'; | |
} | |
@elseif $number == 7 { | |
@return 'svn'; | |
} | |
@elseif $number == 8 { | |
@return 'ght'; | |
} | |
@elseif $number == 9 { | |
@return 'nn'; | |
} | |
@elseif $number == 10 { | |
@return 'tn'; | |
} | |
@elseif $number == 11 { | |
@return 'lvn'; | |
} | |
@elseif $number == 12 { | |
@return 'twlv'; | |
} | |
} | |
@function rowBlocks ($number) { | |
@if $number == 1 or $number == 2 or $number == 3 { | |
@return 3; | |
} | |
@elseif $number == 4 or $number == 5 or $number == 6 { | |
@return 2; | |
} | |
@else { | |
@return 1; | |
} | |
} | |
@function rowMovements ($rowBlocks) { | |
@if $rowBlocks == 3 { | |
@return 5; | |
} | |
@elseif $rowBlocks == 2 { | |
@return 6; | |
} | |
@else { | |
@return 7; | |
} | |
} | |
@function rowSpeed ($row) { | |
@if $row > 10 { | |
@return $speed - 1.5s; | |
} | |
@elseif $row > 8 { | |
@return $speed - 1.25s; | |
} | |
@elseif $row > 5 { | |
@return $speed - 1s; | |
} | |
@elseif $row > 3 { | |
@return $speed - .5s; | |
} | |
@else { | |
@return $speed; | |
} | |
} | |
/** | |
* Light Up | |
**/ | |
@mixin lightUpSelector ($rowName, $row, $activeNumber, $activeBlocks) { | |
$selector: '##{$rowName}-#{$activeBlocks}-#{$activeNumber}:checked ~ .bs .r:nth-of-type(#{$row}) .b:nth-of-type(#{$activeNumber})'; | |
@if $activeBlocks >= 2 { | |
$baseSelector: $selector; | |
$selector: $selector + ', ' + $baseSelector + ' + .b'; | |
@if $activeBlocks == 3 { | |
$selector: $selector + ', ' + $baseSelector + ' + .b + .b'; | |
} | |
} | |
#{$selector} { | |
@content; | |
} | |
}; | |
@mixin targetRemove ($number) { | |
@if $number == 1 { | |
width: $block-size * 2 + $block-gap; | |
} | |
@elseif $number == 2 { | |
width: $block-size; | |
} | |
} | |
@mixin targetAnimations ($row, $blocks) { | |
$targetBottom: (($row - 1) * ($block-size + $block-gap)) + $block-gap; | |
$rowMovements: rowMovements($blocks); | |
$totalMovements: 2 * ($rowMovements - 1); | |
$increments: 100 / $totalMovements; | |
@keyframes target-#{$row}-#{$blocks} { | |
0% { | |
bottom: $targetBottom; | |
transform: translateX(0px); | |
@include targetRemove(3 - $blocks); | |
} | |
@for $i from 1 through $totalMovements { | |
$currentIncrement: $i * $increments; | |
$previousShift: ($i - 1) * ($block-size + $block-gap); | |
$currentShift: $i * ($block-size + $block-gap); | |
@if ($i > ($totalMovements / 2)) { | |
$previousShift: ($totalMovements - ($i - 1)) * ($block-size + $block-gap); | |
$currentShift: ($totalMovements - $i) * ($block-size + $block-gap); | |
} | |
#{$currentIncrement - 0.1}% { | |
transform: translateX($previousShift); | |
} | |
#{$currentIncrement}% { | |
transform: translateX($currentShift); | |
@if $i == $totalMovements { | |
bottom: $targetBottom; | |
@include targetRemove(3 - $blocks); | |
} | |
} | |
} | |
} | |
} | |
@mixin buildElements ($row, $rowBlocks) { | |
$rowName: rowName($row); | |
$rowMovements: rowMovements($rowBlocks); | |
@for $i from 1 through $rowBlocks { | |
@include targetAnimations($row, $i); | |
@for $j from 1 through $rowMovements { | |
@include lightUpSelector($rowName, $row, $j, $i) { | |
@include blockActive; | |
} | |
} | |
} | |
} | |
// Build Elements | |
@for $i from 1 through 12 { | |
$rowBlocks: rowBlocks($i); | |
@include buildElements ($i, $rowBlocks); | |
} | |
/** | |
* Line | |
**/ | |
.line { | |
animation-name: target-1-3; | |
animation-duration: $speed; | |
animation-iteration-count: infinite; | |
bottom: $block-gap; | |
display: flex; | |
left: ($block-gap / 2); | |
overflow: hidden; | |
position: absolute; | |
background: $blocks-active; | |
height: $block-size; | |
margin-right: $block-gap; | |
width: ($block-size + $block-gap) * 2 + $block-size; | |
&:before, | |
&:after { | |
background: $blocks-active; | |
border-left: $block-gap solid $blocks-bg; | |
content: ''; | |
display: block; | |
flex-shrink: 0; | |
height: $block-size; | |
width: $block-size; | |
} | |
&:before { | |
margin-left: #{$block-size}; | |
} | |
} | |
@mixin nextTurn ($selector, $row, $blocks) { | |
$nextRow: $row + 1; | |
$nextRowBlocks: rowBlocks($nextRow); | |
@if $nextRowBlocks > $blocks { | |
$nextRowBlocks: $blocks; | |
} | |
$nextRowSpeed: rowSpeed($row); | |
$important: ''; | |
@if $nextRow > 8 { | |
$important: ' !important'; | |
} | |
#{$selector} ~ .bs .line, | |
#{$selector} ~ .controls .rs { | |
animation-duration: $nextRowSpeed #{$important}; | |
animation-name: target-#{$nextRow}-#{$nextRowBlocks} #{$important}; | |
} | |
$hideSelector: '#{$selector} ~ .controls div[class*="r-#{$row}-"]'; | |
@if $nextRowBlocks >= 2 { | |
$hideSelector: $hideSelector + ', #{$selector} ~ .controls div[class*="r-#{$nextRow}-1"]'; | |
@if $nextRowBlocks > 2 { | |
$hideSelector: $hideSelector + ', #{$selector} ~ .controls div[class*="r-#{$nextRow}-2"]'; | |
} | |
} | |
#{$hideSelector} { | |
display: none; | |
} | |
} | |
/** | |
* Game Logic | |
**/ | |
@mixin gameLogicRemove ($selector, $row, $number, $direction: 'before', $remove: 1) { | |
$removeSelector: '#{$selector} ~ .bs .r:nth-of-type(#{$row}) .b:nth-of-type(#{$number})'; | |
@if $remove == 2 { | |
$secondNumber: $number - 1; | |
@if $direction == 'after' { | |
$secondNumber: $number + 1; | |
} | |
$removeSelector: $removeSelector + ', #{$selector} ~ .bs .r:nth-of-type(#{$row}) .b:nth-of-type(#{$secondNumber})'; | |
} | |
#{$removeSelector} { | |
@include blockInactive; | |
} | |
} | |
@mixin gameLogicOver ($selector) { | |
#{$selector} ~ .results .go { | |
display: flex; | |
} | |
#{$selector} ~ .bs .line { | |
display: none; | |
} | |
} | |
@function gameLogicCheck ($prevBlocks, $nextBlocks, $prevPosition, $nextPosition) { | |
$prevLastPosition: ($prevPosition - 1) + $prevBlocks; | |
$nextLastPosition: ($nextPosition - 1) + $nextBlocks; | |
@if $nextLastPosition < $prevPosition or $nextPosition > $prevLastPosition { | |
@return 'out'; | |
} | |
@elseif $nextPosition == ($prevPosition - 1) { | |
@return '1-before'; | |
} | |
@elseif $nextPosition == ($prevPosition - 2) { | |
@return '2-before'; | |
} | |
@elseif $nextLastPosition == ($prevLastPosition + 1) { | |
@return '1-after'; | |
} | |
@elseif $nextLastPosition == ($prevLastPosition + 2) { | |
@return '2-after'; | |
} | |
@else { | |
@return 'stack'; | |
} | |
} | |
@mixin gameLogic($prevSelector, $blocks, $row, $position) { | |
@if $row < 7 { | |
$nextRow: $row + 1; | |
$nextRowName: rowName($nextRow); | |
$nextRowBlocks: rowBlocks($nextRow); | |
@if $nextRowBlocks > $blocks { | |
$nextRowBlocks: $blocks; | |
} | |
$nextRowMovements: rowMovements($nextRowBlocks); | |
@for $j from 1 through $nextRowMovements { | |
$combinedSelector: '#{$prevSelector} ~ ##{$nextRowName}-#{$nextRowBlocks}-#{$j}:checked'; | |
$status: gameLogicCheck($blocks, $nextRowBlocks, $position, $j); | |
@if $status == '1-before' { | |
@include gameLogicRemove($combinedSelector, $nextRow, $j); | |
@include nextTurn($combinedSelector, $nextRow, $nextRowBlocks - 1); | |
@include gameLogic($combinedSelector, $nextRowBlocks - 1, $nextRow, $j + 1); | |
} | |
@elseif $status == '2-before' { | |
@include gameLogicRemove($combinedSelector, $nextRow, ($j + 1), 'before', 2); | |
@include nextTurn($combinedSelector, $nextRow, $nextRowBlocks - 2); | |
@include gameLogic($combinedSelector, $nextRowBlocks - 2, $nextRow, $j + 2); | |
} | |
@elseif $status == '1-after' { | |
@include gameLogicRemove($combinedSelector, $nextRow, ($j - 1 + $nextRowBlocks), 'after'); | |
@include nextTurn($combinedSelector, $nextRow, $nextRowBlocks - 1); | |
@include gameLogic($combinedSelector, $nextRowBlocks - 1, $nextRow, $j); | |
} | |
@elseif $status == '2-after' { | |
@include gameLogicRemove($combinedSelector, $nextRow, ($j - 2 + $nextRowBlocks), 'after', 2); | |
@include nextTurn($combinedSelector, $nextRow, $nextRowBlocks - 2); | |
@include gameLogic($combinedSelector, $nextRowBlocks - 2, $nextRow, $j); | |
} | |
@elseif $status == 'out' { | |
@include gameLogicOver($combinedSelector); | |
} | |
@elseif $status == 'stack' { | |
@include nextTurn($combinedSelector, $nextRow, $nextRowBlocks); | |
@include gameLogic($combinedSelector, $nextRowBlocks, $nextRow, $j); | |
} | |
} | |
} | |
} | |
// Start | |
@for $i from 1 through 5 { | |
$selector: '#on-3-#{$i}:checked'; | |
@include gameLogic($selector, 3, 1, $i); | |
} | |
*[id^="on-3-"]:checked ~ .bs .line, | |
*[id^="on-3-"]:checked ~ .controls .rs { | |
animation-name: target-2-3; | |
} | |
*[id^="on-3-"]:checked ~ .controls div[class*="r-1-"] { | |
display: none; | |
} | |
// Rows 7 - 12 | |
@for $i from 7 through 11 { | |
$rowName: rowName($i); | |
$nextRowName: rowName($i + 1); | |
@for $j from 1 through 7 { | |
##{$rowName}-1-#{$j}:checked ~ *[id^="#{$nextRowName}-"]:checked:not(##{$nextRowName}-1-#{$j}) ~ .results .go { | |
display: flex; | |
} | |
##{$rowName}-1-#{$j}:checked ~ *[id^="#{$nextRowName}-"]:checked:not(##{$nextRowName}-1-#{$j}) ~ .bs .line { | |
display: none; | |
} | |
$continueSelector: '##{$rowName}-1-#{$j}:checked ~ ##{$nextRowName}-1-#{$j}:checked'; | |
@include nextTurn($continueSelector, ($i + 1), 1); | |
} | |
} | |
// Minor | |
$secondLastRowMinor: rowName(7); | |
$lastRowMinor: rowName(8); | |
@for $i from 1 through 7 { | |
##{$secondLastRowMinor}-1-#{$i}:checked ~ ##{$lastRowMinor}-1-#{$i}:checked ~ .results .go .minor { | |
display: block; | |
} | |
} | |
// Win | |
$secondLastRow: rowName(11); | |
$lastRow: rowName(12); | |
@for $i from 1 through 7 { | |
##{$secondLastRow}-1-#{$i}:checked ~ ##{$lastRow}-1-#{$i}:checked ~ .results .win { | |
display: flex; | |
} | |
} | |
/** | |
* Results | |
**/ | |
.results { | |
.go, | |
.win { | |
color: white; | |
display: none; | |
position: fixed; | |
} | |
.go, | |
.win { | |
align-items: center; | |
background: rgba(0,0,0,.45); | |
bottom: 0; | |
flex-direction: column; | |
font-size: 35px; | |
justify-content: center; | |
left: 0; | |
padding-bottom: 55px; | |
right: 0; | |
text-align: center; | |
text-transform: uppercase; | |
top: 0; | |
span { | |
line-height: 1; | |
} | |
button { | |
appearance: none; | |
background: white; | |
border: none; | |
cursor: pointer; | |
font-family: 'VT323', monospace; | |
font-size: 18px; | |
margin: 20px; | |
opacity: .9; | |
padding: 5px 10px; | |
text-transform: uppercase; | |
} | |
} | |
.go .minor, | |
.win .major { | |
background: url('http://www.jerrylow.com/demo/stacker/minor.png'); | |
background-position: center; | |
background-repeat: no-repeat; | |
background-size: 100% auto; | |
display: none; | |
height: 70px; | |
width: 50px; | |
} | |
.win .major { | |
background-image: url('http://www.jerrylow.com/demo/stacker/major.png'); | |
display: block; | |
height: 100px; | |
width: 50px; | |
} | |
} | |
/** | |
* Controls | |
**/ | |
$control-size: $block-size + $block-gap; | |
$control-bg: #fa7f7f; | |
.controls { | |
bottom: 25px; | |
display: flex; | |
justify-content: center; | |
left: 0; | |
position: absolute; | |
right: 0; | |
&:active { | |
.control .rs, | |
~ .bs .line { | |
animation-play-state: paused; | |
} | |
} | |
.control { | |
background: $control-bg; | |
border: 2px solid $control-bg; | |
border-radius: 5px; | |
height: $control-size + 4px; | |
overflow: hidden; | |
width: $control-size + 4px; | |
} | |
.rs { | |
animation-duration: $speed; | |
animation-name: target-1-3; | |
animation-iteration-count: infinite; | |
} | |
.r { | |
display: flex; | |
flex-direction: row-reverse; | |
white-space: nowrap; | |
&.r-1-1, | |
&.r-1-2, | |
&.r-2-1, | |
&.r-2-2 { | |
display: none; | |
} | |
&[class$="-1"] { | |
margin-left: -#{($block-size + $block-gap) * 6}; | |
} | |
&[class$="-2"] { | |
margin-left: -#{($block-size + $block-gap) * 5}; | |
} | |
&[class$="-3"] { | |
margin-left: -#{($block-size + $block-gap) * 4}; | |
} | |
} | |
@for $i from 1 through 12 { | |
$rowBlocks: rowBlocks($i); | |
@for $j from 1 through $rowBlocks { | |
$movements: rowMovements($j); | |
.r-#{$i}-#{$j} { | |
width: ($block-size + $block-gap) * $movements; | |
} | |
} | |
} | |
label { | |
cursor: pointer; | |
flex-shrink: 0; | |
height: $control-size; | |
width: $control-size; | |
&:first-of-type { | |
margin-right: auto; | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
form | |
- for i in (1..5) | |
input id='on-3-#{i}' type='radio' name='r-1' value='1-#{i}' | |
- for i in (1..5) | |
input id='tw-3-#{i}' type='radio' name='r-2' value='2-#{i}' | |
- for i in (1 .. 3) | |
- last = 5 + (3 - i); | |
- for j in (1 .. last) | |
input id='thr-#{i}-#{j}' type='radio' name='r-3' value='3-#{i}-#{j}' | |
- for h in (4 .. 6) | |
- if h === 4 | |
- name = 'fr' | |
- if h === 5 | |
- name = 'fv' | |
- if h === 6 | |
- name = 'sx' | |
- for i in (1 .. 2) | |
- last = 6 + (2 - i); | |
- for j in (1 .. last) | |
input id='#{name}-#{i}-#{j}' type='radio' name='r-#{h}' value='#{h}-#{i}-#{j}' | |
- for h in (7 .. 12) | |
- if h === 7 | |
- name = 'svn' | |
- if h === 8 | |
- name = 'ght' | |
- if h === 9 | |
- name = 'nn' | |
- if h === 10 | |
- name = 'tn' | |
- if h === 11 | |
- name = 'lvn' | |
- if h === 12 | |
- name = 'twlv' | |
- for i in (1 .. 7) | |
input id='#{name}-1-#{i}' type='radio' name='r-#{h}' value='#{h}-1-#{i}' | |
div.controls | |
div.control | |
div.rs | |
- for h in (1 .. 12) | |
- if h === 1 | |
- name = 'on' | |
- if h === 2 | |
- name = 'tw' | |
- if h === 3 | |
- name = 'thr' | |
- if h === 4 | |
- name = 'fr' | |
- if h === 5 | |
- name = 'fv' | |
- if h === 6 | |
- name = 'sx' | |
- if h === 7 | |
- name = 'svn' | |
- if h === 8 | |
- name = 'ght' | |
- if h === 9 | |
- name = 'nn' | |
- if h === 10 | |
- name = 'tn' | |
- if h === 11 | |
- name = 'lvn' | |
- if h === 12 | |
- name = 'twlv' | |
- if h <= 3 | |
- blocks = 3 | |
- if h > 3 and h <= 6 | |
- blocks = 2 | |
- if h > 6 | |
- blocks = 1 | |
- for i in (1 .. blocks) | |
div class='r r-#{h}-#{i}' | |
- if i === 3 | |
- movements = 5 | |
- if i === 2 | |
- movements = 6 | |
- if i === 1 | |
- movements = 7 | |
- for j in (1 .. movements) | |
label for='#{name}-#{i}-#{j}' | |
div.bs | |
- for i in (1 .. 12) | |
.div.r | |
- for i in (1 .. 7) | |
div.b | |
div.line | |
div.results | |
div.go | |
span | |
| Game | |
br>/ | |
| Over | |
div.minor | |
button Again | |
div.win | |
span | |
| You | |
br>/ | |
| Win | |
div.major | |
button Again | |
h1 Stacker |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment