For the second Opposable Thumbs challenge, I started researching ancient games of chance. It seems that the original dice where the knucklebones taken from livestock like sheep or goats. While you can order the real thing online, I opted to 3D print an open source set because it was quicker. Then I made a wooden box, strapped a piezo sensor to the back, and taught an Arduino to interpret these vibrations numerically. This information is shared with a Processing app, where the numbers are mapped onto various numerical ranges (1-4, 1-6, 1-8, etc.). The system is currently set up for the 7 most common DnD dice, but this is easy to change in the code.
I think the next step for the project is to hook up with the roll20 API. This website allows players to meet in an online space that supports maps, character sheets, and the like. Knucklebones 1.0 would allow such virtual games to retain some tactility by requiring physical rolls, though the objects in question need not be actual dice.
Arduino
/* * Knucklebones 1.0 * Taylor Hokanson 2017 * Based on the work of David Cuartielles and Tom Igoe * http://www.arduino.cc/en/Tutorial/Knock * * Circuit: * + connection of the piezo attached to analog in 0 * - connection of the piezo attached to ground (probably the outer ring on a bare piezo) * 1M resistor attached from analog in 0 to ground * * This example code is in the public domain. */ int rollSensor = A0; int minTrigger = 50; int sensorReading = 0; int seedGen = 0; int threshold = 25; boolean calculatingSeed = false; void setup() { // if you get garbage in the serial monitor increase your baud rate here Serial.begin(115200); delay(100); } void loop() { // detect incoming analog value sensorReading = analogRead(rollSensor); // if that value is larger than the minimum threshold while(sensorReading > threshold){ // start adding up analog values calculatingSeed = true; seedGen = seedGen + sensorReading; // cause loop to exit when analog value drops below threshold sensorReading = analogRead(rollSensor); } if(calculatingSeed == true){ randomSeed(seedGen); if(seedGen < 0){ seedGen = -seedGen; } Serial.println(seedGen); /* here's how you would scale for different dice just with the Arduino int d20 = int(random(1,21)); Serial.println(d20); */ calculatingSeed = false; seedGen = 0; // prevents double-rolls delay(500); } // delays keep serial port from being slammed w/ data delay(100); }
Processing
/* * Knucklebones 1.0 * Taylor Hokanson * based on the work of Andreas Schlegel * www.sojamo.de/libraries/controlp5 */ // GUI stuff import controlP5.*; ControlP5 cp5; // Serial stuff import processing.serial.*; Serial myPort; String val; float floatVal; float oldFloatVal; int rollRange; void setup() { background(220); size(400, 400); cp5 = new ControlP5(this); ButtonBar b = cp5.addButtonBar("bar") .setPosition(0, 0) .setSize(400, 20) .addItems(split("COIN 4 6 8 10 12 20 100"," ")) ; String portName = Serial.list()[3]; myPort = new Serial(this, portName, 115200); textAlign(CENTER, CENTER); textSize(75); fill(255); text("SELECT", width/2, height/2 - 75); text("YOUR", width/2, height/2); text("DIE", width/2, height/2 + 75); } // handle all of the cases where the user clicks on a GUI button void bar(int n) { println("bar clicked, item-value:", n); if(n == 0){ rollRange = 2; }else if(n == 1){ rollRange = 4; }else if(n == 2){ rollRange = 6; }else if(n == 3){ rollRange = 8; }else if(n == 4){ rollRange = 10; }else if(n == 5){ rollRange = 12; }else if(n == 6){ rollRange = 20; }else if(n == 7){ rollRange = 100; } println("Roll range = " + rollRange); background(220); fill(255); textSize(75); text("ROLL!", width/2, height/2 ); } void draw() { if ( myPort.available() > 0) { val = myPort.readStringUntil('\n'); if (val != null) { floatVal = float(val); } } if(floatVal != 0){ float finalRoll = random(1, rollRange + 1); println(int(finalRoll)); background(220); textSize(250); text(int(finalRoll), width/2, height/2 - 20); floatVal = 0; } }