Door Puzzle

https://www.reddit.com/r/dnd/comments/4y4t7l/_/d6l2l6g?context=1000

by jcubed111

HTML

<div id="diskArea"></div>

<br/>

<button onclick="rotate(0, 1)">+ A</button>
<button onclick="rotate(1, 1)">+ B</button>
<button onclick="rotate(2, 1)">+ C</button> <br/>
<button onclick="rotate(0, -1)">- A</button>
<button onclick="rotate(1, -1)">- B</button>
<button onclick="rotate(2, -1)">- C</button>

<br/><br/>
Steps: <span id="steps">0</span>
<br/><br/>
<div id="status"></div>
<br/>
<button onclick="showSolution()">Show Solution</button>
<div id="solution" class="hidden"></div>

<script>
	setup();
	render();
	check();
</script>

CSS

#diskArea{
	position: relative;
	width: 250px;
	height: 250px;
	background: #eee;
}

.disk{
	position: absolute;
	width: 250px;
	height: 250px;
	transform-origin: 50% 50%;
	border-radius: 50%;
	overflow: hidden;
	transition: transform 0.5s;
}

.number{
	position: absolute;
	left: 75px;
	top: 150px;
	width: 100px;
	height: 100px;
	transform-origin: 50px -25px;
	font-size: 40px;
	text-align: center;
	box-sizing: border-box;
	padding-top: 35px;
	border-top-left-radius: 50px 100px;
	border-top-right-radius: 50px 100px;
}

.n0{
	color: transparent;
}

button{ cursor: pointer;}

button,
#solution,
#status{
	font-family: monospace;
}

.hidden{
	display: none;
}

JavaScript

"use strict";

var size = 5; // how many numbers there are per disk
              // 3: easy
			  // 5: moderate
			  // 7: hard
			  // 11: harder
var method = 1; // determines how the buttons work
                // 0: buttons always work the same
				// 1: buttons are randomized when the puzzle is generated,
				//    but each button always moves two diska the same amount.

switch(method) {
	case 0:
		var rotAmounts = [ // always works for size >= 3 if size is prime.
			[0, 1, 1],
			[2, 0, 2],
			[2, 2, 0],
		];
		break;
	case 1:
		var f = [
			((Math.random()*(size>>1))|0)+1,
			((Math.random()*(size>>1))|0)+1,
			((Math.random()*(size>>1))|0)+1,
		];
		var rotAmounts = _.shuffle([
			[1*f[0], 1*f[0], 0*f[0]],
			[0*f[1], 1*f[1], 1*f[1]],
			[1*f[2], 0*f[2], 1*f[2]],
		]);
		break;
}

var offsets = [0, 0, 0];
while(offsets.join('') == '000') { // ensure we never start with a solved puzzle
	offsets = [
		(Math.random()*size)|0,
		(Math.random()*size)|0,
		(Math.random()*size)|0
	];
}

function setup() {
	var colors = ['#ccc', '#bbb', '#aaa'];
	for(var i=2; i>=0; i--) {
		var el = document.createElement('div');
		el.className='disk';
		el.id = ['a', 'b', 'c'][i];
		var angle = 360/size;
		el.style.backgroundImage = `
			linear-gradient(${ +90+angle/2 }deg, ${ colors[i] } 50%, transparent 50%),
    		linear-gradient(${ -90-angle/2 }deg, ${ colors[i] } 50%, transparent 50%),
			linear-gradient(${ +90+angle/2 }deg, #000 50.5%, transparent 50.5%),
    		linear-gradient(${ -90-angle/2 }deg, #000 50.5%, transparent 50.5%)
		`;
		document.getElementById('diskArea').appendChild(el);
		for(var j=0; j<size; j++) {
			var num = document.createElement('div');
			num.className='number' + (j ? '' : ' n0');
			num.style.transform = 'rotate(-' + j/size*360 + 'deg)';
			num.innerHTML = ['a', 'b', 'c'][i] + j;
			el.appendChild(num);
		}
	}
}

function rotate(n, sign) {
	document.getElementById('steps').innerHTML++;
	
	var amounts = rotAmounts[n];
	
	offsets[0] = offsets[0] +...