parent
6a6dac7b01
commit
dab1ed15c1
7 changed files with 198 additions and 0 deletions
|
@ -110,6 +110,12 @@ Sticker by [djuan](https://linktr.ee/mkiiisystem)!
|
|||
|
||||
**[Braceless JS](https://github.com/nycki93/braceless-javascript/), 2018.** You can do a lot with 'one-line' functions. Let's play with that!
|
||||
|
||||
</div>
|
||||
<div class="card">
|
||||
<a href="./mathdice/"><img src="/a/card-mathdice.png"></a>
|
||||
|
||||
**[Mathdice](./mathdice/), 2017.** Practice your number skills with random puzzles. Check with the auto-solver!
|
||||
|
||||
</div>
|
||||
<div class="card">
|
||||
<a href="https://museumofzzt.com/file/p/PortalZZT_v0_5.zip"><img src="/a/card-portal-zzt.png"></a>
|
||||
|
|
BIN
static/a/card-mathdice.png
Normal file
BIN
static/a/card-mathdice.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2 KiB |
Binary file not shown.
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 2.3 KiB |
29
static/mathdice/index.html
Normal file
29
static/mathdice/index.html
Normal file
|
@ -0,0 +1,29 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Mathdice Solver 1.0</title>
|
||||
<!-- By Nick "Nupa" Lamicela, 2018 -->
|
||||
<meta charset="utf-8"/>
|
||||
<link rel="stylesheet" href="mathdice.css"/>
|
||||
<script src="mathdice.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<p>Your numbers are:</p>
|
||||
<form>
|
||||
<input type="text" name="die1" class="scoring dice" />
|
||||
<input type="text" name="die2" class="scoring dice" />
|
||||
<input type="text" name="die3" class="scoring dice" />
|
||||
<input type="text" name="target" id="target" class="dice" />
|
||||
</form>
|
||||
|
||||
<br/>
|
||||
|
||||
<button id="roll-button">Roll dice</button>
|
||||
<button id="solve-button">Solve the puzzle</button>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
<div id="solution"/>
|
||||
</body>
|
||||
</html>
|
19
static/mathdice/mathdice.css
Normal file
19
static/mathdice/mathdice.css
Normal file
|
@ -0,0 +1,19 @@
|
|||
body {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
.dice {
|
||||
display: inline-block;
|
||||
box-sizing: border-box;
|
||||
border: gray outset 6px;
|
||||
text-align: center;
|
||||
|
||||
font-size: 80px;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
#target {
|
||||
border-color: gold;
|
||||
width: 200px;
|
||||
}
|
143
static/mathdice/mathdice.js
Normal file
143
static/mathdice/mathdice.js
Normal file
|
@ -0,0 +1,143 @@
|
|||
"use strict";
|
||||
const OPERATORS = {
|
||||
add: {
|
||||
apply(a, b) {return a + b},
|
||||
toString() {return "+"}
|
||||
},
|
||||
subtract: {
|
||||
apply(a, b) {return a - b},
|
||||
toString() {return "-"}
|
||||
},
|
||||
multiply: {
|
||||
apply(a, b) {return a * b},
|
||||
toString() {return "*"}
|
||||
},
|
||||
divide: {
|
||||
apply(a, b) {return a / b},
|
||||
toString() {return "/"}
|
||||
},
|
||||
power: {
|
||||
apply(a, b) {return a ** b},
|
||||
toString() {return "^"}
|
||||
}
|
||||
}
|
||||
|
||||
// Assign event handlers.
|
||||
document.getElementById("roll-button").onclick = generateProblem;
|
||||
document.getElementById("solve-button").onclick = showSolution;
|
||||
|
||||
/**
|
||||
* Roll dice and generate a mathdice problem. There are three "key" values
|
||||
* (d6), and one "target" roll (1d12 * 1d12).
|
||||
*/
|
||||
function generateProblem () {
|
||||
// mouse:mice :: douse:dice
|
||||
const diceList = document.querySelectorAll('.scoring');
|
||||
Array.prototype.forEach.call(diceList, douse => {
|
||||
douse.value = roll_d(6);
|
||||
})
|
||||
|
||||
const target = document.getElementById('target');
|
||||
target.value = roll_d(12) * roll_d(12);
|
||||
}
|
||||
|
||||
/**
|
||||
* Solve the problem! Read in the values on screen and run them through the
|
||||
* solution algorithm.
|
||||
*/
|
||||
function showSolution () {
|
||||
const diceList = document.querySelectorAll('.scoring');
|
||||
const diceValues = Array.prototype.map.call(diceList, douse =>
|
||||
Number(douse.value)
|
||||
)
|
||||
const targetValue = Number(document.getElementById('target').value);
|
||||
const solution = solve(diceValues, targetValue);
|
||||
document.getElementById('solution').innerHTML = solution;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a random number between 1 and n, inclusive.
|
||||
* @param {number} n
|
||||
*/
|
||||
function roll_d (n) {
|
||||
const roll = Math.floor(n * Math.random()) + 1;
|
||||
return roll;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate an expression! An expression is either
|
||||
* 1) a number, or
|
||||
* 2) two expressions connected with an operator.
|
||||
* @param {number | Array} expr
|
||||
* @returns {number}
|
||||
*/
|
||||
function evaluateExpression (expr) {
|
||||
if (!Array.isArray(expr)) return expr;
|
||||
|
||||
const [a, op, b] = expr;
|
||||
const valueA = evaluateExpression(a);
|
||||
const valueB = evaluateExpression(b);
|
||||
return op.apply(valueA, valueB);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write an expression as a string.
|
||||
* @param {number | Array} expr
|
||||
* @returns {string}
|
||||
*/
|
||||
function writeExpression (expr) {
|
||||
if (!Array.isArray(expr)) return expr;
|
||||
|
||||
const [a, op, b] = expr;
|
||||
let strA = writeExpression(a);
|
||||
if (Array.isArray(a)) {strA = `(${strA})`}
|
||||
let strB = writeExpression(b);
|
||||
if (Array.isArray(b)) {strB = `(${strB})`}
|
||||
return `${strA} ${op.toString()} ${strB}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Solve a mathdice problem!
|
||||
* @param {number[]} scoringDice An array of "key" values used in the puzzle.
|
||||
* @param {number} target The "target" value to try and produce.
|
||||
* @returns {string} A string representation of the optimal answer.
|
||||
*/
|
||||
function solve(scoringDice, target) {
|
||||
// Possible solutions: 2 * 3! * 5^2 = 300. Reasonable to brute force.
|
||||
const [a, b, c] = scoringDice;
|
||||
const dicePermutations = [
|
||||
[a, b, c],
|
||||
[a, c, b],
|
||||
[b, a, c],
|
||||
[b, c, a],
|
||||
[c, a, b],
|
||||
[c, b, a]
|
||||
];
|
||||
const templates = [
|
||||
(a, b, c, x, y) => [a, x, [b, y, c]],
|
||||
(a, b, c, x, y) => [[a, x, b], y, c]
|
||||
];
|
||||
|
||||
// I don't even care how brutally hard-coded this is, I don't NEED the
|
||||
// general case right now.
|
||||
const guesses = [];
|
||||
for (const keyX in OPERATORS) {
|
||||
const x = OPERATORS[keyX];
|
||||
for (const keyY in OPERATORS) {
|
||||
const y = OPERATORS[keyY];
|
||||
dicePermutations.forEach( ([a, b, c]) => {
|
||||
templates.forEach( f => {
|
||||
guesses.push(f(a, b, c, x, y));
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Find the best guess.
|
||||
const scores = guesses.map( guess =>
|
||||
Math.abs(target - evaluateExpression(guess))
|
||||
)
|
||||
const bestScore = Math.min(...scores);
|
||||
const bestGuess = guesses[scores.indexOf(bestScore)];
|
||||
return `${writeExpression(bestGuess)} = ${evaluateExpression(bestGuess)}`;
|
||||
}
|
1
static/mathdice/readme.md
Normal file
1
static/mathdice/readme.md
Normal file
|
@ -0,0 +1 @@
|
|||
An html mathdice game.
|
Loading…
Add table
Reference in a new issue