choozi - Recombine code for B1/B2 and use setUI

master
Martin Boonk 2022-12-18 15:42:35 +01:00
parent bca6722a1b
commit 7c8d941e69
4 changed files with 35 additions and 240 deletions

View File

@ -11,16 +11,21 @@ the players seated in a circle, set the number of segments equal to the number
of players, ensure that each person knows which colour represents them, and then of players, ensure that each person knows which colour represents them, and then
choose a segment. After a short animation, the chosen segment will fill the screen. choose a segment. After a short animation, the chosen segment will fill the screen.
You can use Choozi to randomly select an element from any set with 2 to 13 members, You can use Choozi to randomly select an element from any set with 2 to 14 members,
as long as you can define a bijection between members of the set and coloured as long as you can define a bijection between members of the set and coloured
segments on the Bangle.js display. segments on the Bangle.js display.
## Controls ## Controls Bangle 1
BTN1: increase the number of segments BTN1: increase the number of segments
BTN2: choose a segment at random BTN2: choose a segment at random
BTN3: decrease the number of segments BTN3: decrease the number of segments
## Controls Bangle 2
Swipe up/down: increase/decrease the number of segments
BTN1 or tap: choose a segment at random
## Creator ## Creator
James Stanley James Stanley

View File

@ -5,14 +5,15 @@
* James Stanley 2021 * James Stanley 2021
*/ */
var colours = ['#ff0000', '#ff8080', '#00ff00', '#80ff80', '#0000ff', '#8080ff', '#ffff00', '#00ffff', '#ff00ff', '#ff8000', '#ff0080', '#8000ff', '#0080ff']; var colours = ['#ff0000', '#00ff00', '#0000ff', '#ffff00', '#00ffff', '#ff00ff', '#ffffff'];
var colours2 = ['#808080', '#800080', '#808000', '#008080', '#ff4040', '#40ff40', '#4040ff'];
var stepAngle = 0.18; // radians - resolution of polygon var stepAngle = 0.18; // radians - resolution of polygon
var gapAngle = 0.035; // radians - gap between segments var gapAngle = 0.035; // radians - gap between segments
var perimMin = 110; // px - min. radius of perimeter var perimMin = g.getWidth()*0.44; // px - min. radius of perimeter
var perimMax = 120; // px - max. radius of perimeter var perimMax = g.getWidth()*0.49; // px - max. radius of perimeter
var segmentMax = 106; // px - max radius of filled-in segment var segmentMax = g.getWidth()*0.42; // px - max radius of filled-in segment
var segmentStep = 5; // px - step size of segment fill animation var segmentStep = 5; // px - step size of segment fill animation
var circleStep = 4; // px - step size of circle fill animation var circleStep = 4; // px - step size of circle fill animation
@ -22,10 +23,10 @@ var minSpeed = 0.001; // rad/sec
var animStartSteps = 300; // how many steps before it can start slowing? var animStartSteps = 300; // how many steps before it can start slowing?
var accel = 0.0002; // rad/sec/sec - acc-/deceleration rate var accel = 0.0002; // rad/sec/sec - acc-/deceleration rate
var ballSize = 3; // px - ball radius var ballSize = 3; // px - ball radius
var ballTrack = 100; // px - radius of ball path var ballTrack = g.getWidth()*0.40; // px - radius of ball path
var centreX = 120; // px - centre of screen var centreX = g.getWidth()*0.5; // px - centre of screen
var centreY = 120; // px - centre of screen var centreY = g.getWidth()*0.5; // px - centre of screen
var fontSize = 50; // px var fontSize = 50; // px
@ -33,7 +34,6 @@ var radians = 2*Math.PI; // radians per circle
var defaultN = 3; // default value for N var defaultN = 3; // default value for N
var minN = 2; var minN = 2;
var maxN = colours.length;
var N; var N;
var arclen; var arclen;
@ -82,6 +82,7 @@ function arc(minR, maxR, minAngle, maxAngle) {
// draw the arc segments around the perimeter // draw the arc segments around the perimeter
function drawPerimeter() { function drawPerimeter() {
g.setBgColor('#000000');
g.clear(); g.clear();
for (var i = 0; i < N; i++) { for (var i = 0; i < N; i++) {
g.setColor(colours[i%colours.length]); g.setColor(colours[i%colours.length]);
@ -131,6 +132,7 @@ function animateChoice(target) {
g.fillCircle(x, y, ballSize); g.fillCircle(x, y, ballSize);
oldx=x; oldx=x;
oldy=y; oldy=y;
if (process.env.HWVERSION == 2) g.flip();
} }
} }
@ -186,23 +188,23 @@ function readN() {
} }
shuffle(colours); // is this really best? shuffle(colours); // is this really best?
Bangle.setLCDMode("direct"); shuffle(colours2);
Bangle.setLCDTimeout(0); // keep screen on colours=colours.concat(colours2);
var maxN = colours.length;
if (process.env.HWVERSION == 1){
Bangle.setLCDMode("direct");
Bangle.setLCDTimeout(0); // keep screen on
}
readN(); readN();
drawN(); drawN();
setWatch(() => { Bangle.setUI("updown", (v)=>{
setN(N+1); if (!v){
drawN(); writeN();
}, BTN1, {repeat:true}); drawPerimeter();
choose();
setWatch(() => { } else {
writeN(); setN(N+v);
drawPerimeter(); drawN();
choose(); }
}, BTN2, {repeat:true}); });
setWatch(() => {
setN(N-1);
drawN();
}, BTN3, {repeat:true});

View File

@ -1,211 +0,0 @@
/* Choozi - Choose people or things at random using Bangle.js.
* Inspired by the "Chwazi" Android app
*
* James Stanley 2021
*/
var colours = ['#ff0000', '#00ff00', '#0000ff', '#ffff00', '#00ffff', '#ff00ff', '#ffffff'];
var colours2 = ['#808080', '#800080', '#808000', '#008080', '#ff4040', '#40ff40', '#4040ff'];
var stepAngle = 0.18; // radians - resolution of polygon
var gapAngle = 0.035; // radians - gap between segments
var perimMin = 80; // px - min. radius of perimeter
var perimMax = 87; // px - max. radius of perimeter
var segmentMax = 70; // px - max radius of filled-in segment
var segmentStep = 5; // px - step size of segment fill animation
var circleStep = 4; // px - step size of circle fill animation
// rolling ball animation:
var maxSpeed = 0.08; // rad/sec
var minSpeed = 0.001; // rad/sec
var animStartSteps = 300; // how many steps before it can start slowing?
var accel = 0.0002; // rad/sec/sec - acc-/deceleration rate
var ballSize = 3; // px - ball radius
var ballTrack = 75; // px - radius of ball path
var centreX = 88; // px - centre of screen
var centreY = 88; // px - centre of screen
var fontSize = 50; // px
var radians = 2*Math.PI; // radians per circle
var defaultN = 3; // default value for N
var minN = 2;
var N;
var arclen;
// https://www.frankmitchell.org/2015/01/fisher-yates/
function shuffle(array) {
var i = 0
, j = 0
, temp = null;
for (i = array.length - 1; i > 0; i -= 1) {
j = Math.floor(Math.random() * (i + 1));
temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
// draw an arc between radii minR and maxR, and between
// angles minAngle and maxAngle
function arc(minR, maxR, minAngle, maxAngle) {
var step = stepAngle;
var angle = minAngle;
var inside = [];
var outside = [];
var c, s;
while (angle < maxAngle) {
c = Math.cos(angle);
s = Math.sin(angle);
inside.push(centreX+c*minR); // x
inside.push(centreY+s*minR); // y
// outside coordinates are built up in reverse order
outside.unshift(centreY+s*maxR); // y
outside.unshift(centreX+c*maxR); // x
angle += step;
}
c = Math.cos(maxAngle);
s = Math.sin(maxAngle);
inside.push(centreX+c*minR);
inside.push(centreY+s*minR);
outside.unshift(centreY+s*maxR);
outside.unshift(centreX+c*maxR);
var vertices = inside.concat(outside);
g.fillPoly(vertices, true);
}
// draw the arc segments around the perimeter
function drawPerimeter() {
g.setBgColor('#000000');
g.clear();
for (var i = 0; i < N; i++) {
g.setColor(colours[i%colours.length]);
var minAngle = (i/N)*radians;
arc(perimMin,perimMax,minAngle,minAngle+arclen);
}
}
// animate a ball rolling around and settling at "target" radians
function animateChoice(target) {
var angle = 0;
var speed = 0;
var oldx = -10;
var oldy = -10;
var decelFromAngle = -1;
var allowDecel = false;
for (var i = 0; true; i++) {
angle = angle + speed;
if (angle > radians) angle -= radians;
if (i < animStartSteps || (speed < maxSpeed && !allowDecel)) {
speed = speed + accel;
if (speed > maxSpeed) {
speed = maxSpeed;
/* when we reach max speed, we know how long it takes
* to accelerate, and therefore how long to decelerate, so
* we can work out what angle to start decelerating from */
if (decelFromAngle < 0) {
decelFromAngle = target-angle;
while (decelFromAngle < 0) decelFromAngle += radians;
while (decelFromAngle > radians) decelFromAngle -= radians;
}
}
} else {
if (!allowDecel && (angle < decelFromAngle) && (angle+speed >= decelFromAngle)) allowDecel = true;
if (allowDecel) speed = speed - accel;
if (speed < minSpeed) speed = minSpeed;
if (speed == minSpeed && angle < target && angle+speed >= target) return;
}
var r = i/2;
if (r > ballTrack) r = ballTrack;
var x = centreX+Math.cos(angle)*r;
var y = centreY+Math.sin(angle)*r;
g.setColor('#000000');
g.fillCircle(oldx,oldy,ballSize+1);
g.setColor('#ffffff');
g.fillCircle(x, y, ballSize);
oldx=x;
oldy=y;
g.flip();
}
}
// choose a winning segment and animate its selection
function choose() {
var chosen = Math.floor(Math.random()*N);
var minAngle = (chosen/N)*radians;
var maxAngle = minAngle + arclen;
animateChoice((minAngle+maxAngle)/2);
g.setColor(colours[chosen%colours.length]);
for (var i = segmentMax-segmentStep; i >= 0; i -= segmentStep)
arc(i, perimMax, minAngle, maxAngle);
arc(0, perimMax, minAngle, maxAngle);
for (var r = 1; r < segmentMax; r += circleStep)
g.fillCircle(centreX,centreY,r);
g.fillCircle(centreX,centreY,segmentMax);
}
// draw the current value of N in the middle of the screen, with
// up/down arrows
function drawN() {
g.setColor('#ffffff');
g.setFont("Vector",fontSize);
g.drawString(N,centreX-g.stringWidth(N)/2+4,centreY-fontSize/2);
if (N < maxN)
g.fillPoly([centreX-6,centreY-fontSize/2-7, centreX+6,centreY-fontSize/2-7, centreX, centreY-fontSize/2-14]);
if (N > minN)
g.fillPoly([centreX-6,centreY+fontSize/2+5, centreX+6,centreY+fontSize/2+5, centreX, centreY+fontSize/2+12]);
}
// update number of segments, with min/max limit, "arclen" update,
// and screen reset
function setN(n) {
N = n;
if (N < minN) N = minN;
if (N > maxN) N = maxN;
arclen = radians/N - gapAngle;
drawPerimeter();
}
// save N to choozi.txt
function writeN() {
var file = require("Storage").open("choozi.txt","w");
file.write(N);
}
// load N from choozi.txt
function readN() {
var file = require("Storage").open("choozi.txt","r");
var n = file.readLine();
if (n !== undefined) setN(parseInt(n));
else setN(defaultN);
}
shuffle(colours); // is this really best?
shuffle(colours2);
colours=colours.concat(colours2);
var maxN = colours.length;
readN();
drawN();
setWatch(() => {
writeN();
drawPerimeter();
choose();
}, BTN1, {repeat:true});
Bangle.on('touch', function(zone,e) {
if(e.x>+88){
setN(N-1);
drawN();
}else{
setN(N+1);
drawN();
}
});

View File

@ -10,8 +10,7 @@
"allow_emulator": true, "allow_emulator": true,
"screenshots": [{"url":"bangle1-choozi-screenshot1.png"},{"url":"bangle1-choozi-screenshot2.png"}], "screenshots": [{"url":"bangle1-choozi-screenshot1.png"},{"url":"bangle1-choozi-screenshot2.png"}],
"storage": [ "storage": [
{"name":"choozi.app.js","url":"app.js","supports": ["BANGLEJS"]}, {"name":"choozi.app.js","url":"app.js"},
{"name":"choozi.app.js","url":"appb2.js","supports": ["BANGLEJS2"]},
{"name":"choozi.img","url":"app-icon.js","evaluate":true} {"name":"choozi.img","url":"app-icon.js","evaluate":true}
] ]
} }