mirror of https://github.com/espruino/BangleApps
164 lines
5.2 KiB
JavaScript
164 lines
5.2 KiB
JavaScript
|
var settings = require("Storage").readJSON("sudoku.json",1)||{};
|
||
|
|
||
|
const Layout = require("Layout");
|
||
|
var puzzle = ""; // start puzzle space for blanks, 81 char str
|
||
|
var solution = ""; // the solution for the puzzle 81 char str
|
||
|
var current = ""; // current puzzle state, 81 char str
|
||
|
var sx=4, sy=4; // selected item
|
||
|
var dx=0, dy=0, dmoved=false; // current drag amount
|
||
|
const DRAG = 20;
|
||
|
const CHOOSERFONT = "12x20:2";
|
||
|
const COL_GOOD = "#00F";
|
||
|
const COL_BAD = "#F00";
|
||
|
|
||
|
function saveSettings() {
|
||
|
settings.puzzle = puzzle;
|
||
|
settings.solution = solution;
|
||
|
settings.current = current;
|
||
|
require("Storage").writeJSON("sudoku.json",settings);
|
||
|
}
|
||
|
|
||
|
function startGame(difficulty) {
|
||
|
let file = require("Storage").read("sudoku.easy.txt");
|
||
|
let games = 0|(file.length/163);
|
||
|
let game = Math.floor(Math.random()*games);
|
||
|
let line = file.substr(game*163, 162);
|
||
|
puzzle = line.substr(0,81).replaceAll("-"," ");
|
||
|
current = ""+puzzle; // new string
|
||
|
solution = line.substr(81,81);
|
||
|
showGrid();
|
||
|
}
|
||
|
|
||
|
function draw() {
|
||
|
g.clear(1);
|
||
|
for (var i=0;i<10;i++) {
|
||
|
g.setColor((i%3)?"#888":g.theme.fg);
|
||
|
g.fillRect(7+18*i, 7, 7+18*i, 169);
|
||
|
g.fillRect(7, 7+18*i, 169, 7+18*i);
|
||
|
}
|
||
|
i = 0;
|
||
|
g.setFont("6x8:2").setFontAlign(0,0);
|
||
|
for (var y=0;y<9;y++)
|
||
|
for (var x=0;x<9;x++)
|
||
|
g.setColor(puzzle[i]==" "?((solution[i]==current[i])?COL_GOOD:COL_BAD):g.theme.fg).drawString(current[i++], 16 + 18*x, 16 + 18*y);
|
||
|
}
|
||
|
function drawSq(x,y,sel) {
|
||
|
g.setColor(sel ? g.theme.bgH : g.theme.bg).fillRect(8+x*18,8+y*18,24+x*18,24+y*18);
|
||
|
g.setFont("6x8:2").setFontAlign(0,0).setColor(sel ? g.theme.fgH : g.theme.fg);
|
||
|
var i = x+y*9;
|
||
|
g.setColor(puzzle[i]==" "?((solution[i]==current[i])?COL_GOOD:COL_BAD):g.theme.fg).drawString(current[i++], 16 + 18*x, 16 + 18*y);
|
||
|
}
|
||
|
|
||
|
// precalculate our layout
|
||
|
var choose = function(num) {
|
||
|
if (num!==undefined) {
|
||
|
Bangle.buzz(100);
|
||
|
var i = sx+sy*9;
|
||
|
current = current.substr(0,i)+num+current.substr(i+1);
|
||
|
}
|
||
|
showGrid();
|
||
|
if (current == solution) {
|
||
|
saveSettings();
|
||
|
E.showMessage(/*LANG*/"Well done,\nyou finished!", {
|
||
|
title:"Sudoku",
|
||
|
buttons : {"Ok":true},
|
||
|
img : require("heatshrink").decompress(atob("k8pwcBkmSpICCnVp0IIFAQl27dt2wOJ9On7/582YtOJBws7tv/DoQKCBwubvvmzVp0wCCHAvf9u2DpE8uOn7waEjQdDq1Itv+DQQCB0gZBkMkyPHj1tHAuGAQOJktt/Ubvo4DAQI3ChMcuPHzd5HAeasICBtFOr/kw3fDQe27RhCofH/lx4xxFSQIFBiu/rNhHAp0DyHx447BHAh0Ekt/61YlodFOgaPBDoJWFAQeJrf1yySBDohWCoVJOgUcKw+YyVOrtly0LKw4eBp4dCjytHBwNOrNlyUbDoVpHAYDBo4dH02EFoTpBDoQ7B2MyZALMCHYsGtOOjALDAQQ7CyM6tFkFIgCDDoPBnkT4wLFAQegxP8z/JBxNwp8E+ALHAQMl+V/n4aIAQI4B4McBxU/z/P8gOKjnx4JHJAQP//5WKSoVwDRICBv//FJTRDBxeT/5WLkmR45WLkitLARNIA"))
|
||
|
}).then(showMenu);
|
||
|
}
|
||
|
};
|
||
|
var numberChooser = new Layout( {
|
||
|
type:"v", c: [
|
||
|
{type:"h", c: [
|
||
|
{type:"btn", font:CHOOSERFONT, label:"1", fillx:1, cb:()=>choose(1) },
|
||
|
{type:"btn", font:CHOOSERFONT, label:"2", fillx:1, cb:()=>choose(2) },
|
||
|
{type:"btn", font:CHOOSERFONT, label:"3", fillx:1, cb:()=>choose(3) }
|
||
|
]},
|
||
|
{type:"h", c: [
|
||
|
{type:"btn", font:CHOOSERFONT, label:"4", fillx:1, cb:()=>choose(4) },
|
||
|
{type:"btn", font:CHOOSERFONT, label:"5", fillx:1, cb:()=>choose(5) },
|
||
|
{type:"btn", font:CHOOSERFONT, label:"6", fillx:1, cb:()=>choose(6) }
|
||
|
]},
|
||
|
{type:"h", c: [
|
||
|
{type:"btn", font:CHOOSERFONT, label:"7", fillx:1, cb:()=>choose(7) },
|
||
|
{type:"btn", font:CHOOSERFONT, label:"8", fillx:1, cb:()=>choose(8) },
|
||
|
{type:"btn", font:CHOOSERFONT, label:"9", fillx:1, cb:()=>choose(9) }
|
||
|
]}
|
||
|
], fillx:1
|
||
|
}, {btns:[ {label:/*LANG*/"Back", cb: l=>choose()} ]});
|
||
|
|
||
|
function showGrid() {
|
||
|
Bangle.setUI({mode:"custom", drag:e=>{
|
||
|
if (e.b) {
|
||
|
dx += e.dx;
|
||
|
dy += e.dy;
|
||
|
while (Math.abs(dx) >= DRAG) {
|
||
|
Bangle.buzz(40);
|
||
|
let old = sx;
|
||
|
sx = (sx+9+Math.sign(dx))%9;
|
||
|
dx -= Math.sign(dx)*DRAG;
|
||
|
drawSq(old,sy,0);
|
||
|
drawSq(sx,sy,1);
|
||
|
dmoved = true;
|
||
|
}
|
||
|
while (Math.abs(dy) >= DRAG) {
|
||
|
Bangle.buzz(40);
|
||
|
let old = sy;
|
||
|
sy = (sy+9+Math.sign(dy))%9;
|
||
|
dy -= Math.sign(dy)*DRAG;
|
||
|
drawSq(sx,old,0);
|
||
|
drawSq(sx,sy,1);
|
||
|
dmoved = true;
|
||
|
}
|
||
|
} else {
|
||
|
if (!dmoved && puzzle[sx+sy*9]==" ") {
|
||
|
Bangle.buzz(100);
|
||
|
setTimeout(showSelectNumber, 10);
|
||
|
}
|
||
|
dx = dy = 0;
|
||
|
dmoved = false;
|
||
|
}
|
||
|
}, btn:() => {
|
||
|
saveSettings();
|
||
|
showMenu();
|
||
|
}});
|
||
|
draw();
|
||
|
drawSq(sx,sy,1);
|
||
|
}
|
||
|
|
||
|
function showSelectNumber() {
|
||
|
g.clear();
|
||
|
numberChooser.setUI();
|
||
|
numberChooser.render();
|
||
|
}
|
||
|
|
||
|
function showMenu() {
|
||
|
var menu = { "" : { title: "Sudoku" } };
|
||
|
if (settings.puzzle)
|
||
|
menu["Resume Game"] = () => {
|
||
|
puzzle = settings.puzzle;
|
||
|
solution = settings.solution;
|
||
|
current = settings.current;
|
||
|
showGrid();
|
||
|
};
|
||
|
menu["New Game"] = () => {
|
||
|
E.showMenu({ "" : { title: /*LANG*/"Difficulty", back : showMenu },
|
||
|
"Easy" : () => startGame("easy"),
|
||
|
"Medium" : () => startGame("medium"),
|
||
|
"Hard" : () => startGame("hard")
|
||
|
});
|
||
|
};
|
||
|
menu["Exit"] = () => load();
|
||
|
E.showMenu(menu);
|
||
|
}
|
||
|
|
||
|
if (settings.puzzle) {
|
||
|
puzzle = settings.puzzle;
|
||
|
solution = settings.solution;
|
||
|
current = settings.current;
|
||
|
showGrid();
|
||
|
} else {
|
||
|
showMenu();
|
||
|
}
|
||
|
|
||
|
E.on("kill", saveSettings); // ensure we save the game
|