For a course in the Industrial Design Master, we have to use communities in every step of a design face. Hence why I wanted to ask this probably straightforward question in this forum.
As a group, we want to give the physical board game Risk some digital elements. We went to a Risk community to ask which elements we should add to the physical game and a few people mentioned that they would like to skip rolling dice. Therefore we want to program digital dice, as in we want to let the Arduino generate random numbers between 1 and 6 per dice roll, add those up and provide the player with the total score.
As you can roll up to 3 dice in Risk without consistently rolling 3 dice every time but also having to roll 2 or 1 dice I was wondering what is the best option to get input as to how many dice there need to be rolled. I was thinking of a button counter that being if you need to roll 2 dice you press the button twice. Would this be the best solution or are there better options to give this kind of input as a player?
I haven't started the code just yet, because we need community involvement, so this is a preliminary question.
Thank you in advance!!
Kind regarts,
Nina Bremmers
Be aware that the Arduino can not generate truly random numbers. You need to seed the generator with some starting random number. The most reliable way, is to count continuously (not displayed) while you wait for a user event like a button push. You only need to do this once, at the beginning of the program.
It's possible to improve a little bit on the analog port read method:
// random seed 1.04
//
// produces a generally random number based on processing
// error voltages from the readings taken from the analog to digital
// conversion port
// on the Arduino
// 2016-02-17 32 bit test/demo version
const byte analogPort = A1;
const int BUCKET_SIZE = 64;
int buckets[BUCKET_SIZE];
void setup() {
Serial.begin(9600);
}
void loop() {
long temp = getRandomSeed(31);
buckets[temp%BUCKET_SIZE]++;
for (int i = 0; i < BUCKET_SIZE; i++)
{
if (buckets[i] == 0)
{
Serial.print(' ');
}
else
{
Serial.print(buckets[i]);
}
Serial.print(' ');
}
Serial.println('*');
}
long getRandomSeed(int numBits)
{
// magic numbers tested 2016-03-28
// try to speed it up
// Works Well. Keep!
//
if (numBits > 31 or numBits <1) numBits = 31; // limit input range
const int baseIntervalMs = 1UL; // minumum wait time
const byte sampleSignificant = 7; // modulus of the input sample
const byte sampleMultiplier = 10; // ms per sample digit difference
const byte hashIterations = 3;
int intervalMs = 0;
unsigned long reading;
long result = 0;
int tempBit = 0;
Serial.print("randomizing...");
pinMode(analogPort, INPUT_PULLUP);
pinMode(analogPort, INPUT);
delay(200);
// Now there will be a slow decay of the voltage,
// about 8 seconds
// so pick a point on the curve
// offset by the processed previous sample:
//
for (int bits = 0; bits < numBits; bits++)
{
Serial.print('*');
for (int i = 0; i < hashIterations; i++)
{
// Serial.print(' ');
// Serial.print( hashIterations - i );
delay(baseIntervalMs + intervalMs);
// take a sample
reading = analogRead(analogPort);
tempBit ^= reading & 1;
// take the low "digits" of the reading
// and multiply it to scale it to
// map a new point on the decay curve:
intervalMs = (reading % sampleSignificant) * sampleMultiplier;
}
result |= (long)(tempBit & 1) << bits;
}
Serial.println(result);
// Serial.println();
return result;
}
long randNummer = random(500);
Serial.println(randNummer);
So the 500 would than be 18 as that is the maximum amount you can roll with 3 dice, or 12 and 6 for 2 or 1 dice. But this would than not be accurate enough?
random(18) gives a number between 0 and 17 inclusive.
It's just wrong. Three dice can make 18, but the probability of any number 0 to 18 is not the same.
You have to "roll" three dice
byte d1 = random(6) + 1;
so here d1 will take on values 1..6 inclusive.
Get three rolls and add them up to get a proper disttribution of values from 3..18 inclusive.
As for accuracy or "randomness", you can go to some minor trouble and be OK for fun and games. Anything more serious like Vegas or cryptography will need a bit more care.
So for the sake of the opponent rolling dice as well it might be easiest to add a 4th button to loop the dice rolling again but without losing the first total dice value?
say something like:
press button amount of dice attacker
val1 = attacker
press button 4
press button amount of dice opponent
val2 = opponent
I now have this as code. It might be a bit unhandy due to the size but I think it works the way it's supposed to?
const int buttonPin4 = 2; // the number of the pushbutton pin
const int buttonPin3 = 3;
const int buttonPin2 = 4;
const int buttonPin1 = 5;
// Variables will change:
int buttonState4; // the current reading from the input pin
int lastButtonState4 = LOW; // the previous reading from the input pin
int buttonState3;
int lastButtonState3 = LOW;
int buttonState2;
int lastButtonState2 = LOW;
int buttonState1;
int lastButtonState1 = LOW;
// the following variables are unsigned longs because the time, measured in
// milliseconds, will quickly become a bigger number than can be stored in an int.
unsigned long debounceDelay = 50; // the debounce time; increase if the output flickers
unsigned long lastDebounceTime4 = 0; // the last time the output pin was toggled
unsigned long lastDebounceTime3 = 0;
unsigned long lastDebounceTime2 = 0;
unsigned long lastDebounceTime1 = 0;
int x = 0;
long attacker;
long opponent;
void setup() {
Serial.begin(9600);
pinMode(buttonPin4, INPUT);
pinMode(buttonPin3, INPUT);
pinMode(buttonPin2, INPUT);
pinMode(buttonPin1, INPUT);
}
void loop() {
// read the state of the switch into a local variable:
int reading4 = digitalRead(buttonPin4);
int reading3 = digitalRead(buttonPin3);
int reading2 = digitalRead(buttonPin2);
int reading1 = digitalRead(buttonPin1);
// check to see if you just pressed the button
// (i.e. the input went from LOW to HIGH), and you've waited long enough
// since the last press to ignore any noise:
// If the switch changed, due to noise or pressing:
if (reading4 != lastButtonState4) {
// reset the debouncing timer
lastDebounceTime4 = millis();
}
if (reading3 != lastButtonState3) {
// reset the debouncing timer
lastDebounceTime3 = millis();
}
if (reading2 != lastButtonState2) {
// reset the debouncing timer
lastDebounceTime2 = millis();
}
if (reading1 != lastButtonState1) {
// reset the debouncing timer
lastDebounceTime1 = millis();
}
if ((millis() - lastDebounceTime4) > debounceDelay) {
// whatever the reading is at, it's been there for longer than the debounce
// delay, so take it as the actual current state:
// if the button state has changed:
if (reading4 != buttonState4) {
buttonState4 = reading4;
// only toggle the LED if the new button state is HIGH
if (buttonState4 == HIGH) {
x++;
}
if (x >= 2) {
x = 0;
}
}
}
// save the reading. Next time through the loop, it'll be the lastButtonState:
lastButtonState4 = reading4;
if (x == 0) {
if ((millis() - lastDebounceTime3) > debounceDelay) {
// whatever the reading is at, it's been there for longer than the debounce
// delay, so take it as the actual current state:
// if the button state has changed:
if (reading3 != buttonState3) {
buttonState3 = reading3;
if (buttonState3 == HIGH) {
attacker = random(1,7); // generates random numbers 1 till 6 to stimulate a single dice roll
Serial.print("attacker: ");
Serial.print(attacker);
Serial.print(" Vs. ");
}
}
}
lastButtonState3 = reading3;
if ((millis() - lastDebounceTime2) > debounceDelay) {
// whatever the reading is at, it's been there for longer than the debounce
// delay, so take it as the actual current state:
// if the button state has changed:
if (reading2 != buttonState2) {
buttonState2 = reading2;
if (buttonState2 == HIGH) {
attacker = random(2,13); // generates random numbers 2 till 12 to stimulate a double dice roll
Serial.print("attacker: ");
Serial.print(attacker);
Serial.print(" Vs. ");
}
}
}
lastButtonState2 = reading2;
if ((millis() - lastDebounceTime1) > debounceDelay) {
// whatever the reading is at, it's been there for longer than the debounce
// delay, so take it as the actual current state:
// if the button state has changed:
if (reading1 != buttonState1) {
buttonState1 = reading1;
if (buttonState1 == HIGH) {
attacker = random(3,19); // generates random numbers 3 till 18 to stimulate a triple dice roll
Serial.print("attacker: ");
Serial.print(attacker);
Serial.print(" Vs. ");
}
}
}
lastButtonState1 = reading1;
}
if (x == 1) {
if ((millis() - lastDebounceTime3) > debounceDelay) {
// whatever the reading is at, it's been there for longer than the debounce
// delay, so take it as the actual current state:
// if the button state has changed:
if (reading3 != buttonState3) {
buttonState3 = reading3;
if (buttonState3 == HIGH) {
opponent = random(1,7);
Serial.print("opponent: ");
Serial.print(opponent);
Serial.println();
}
}
}
lastButtonState3 = reading3;
if ((millis() - lastDebounceTime2) > debounceDelay) {
// whatever the reading is at, it's been there for longer than the debounce
// delay, so take it as the actual current state:
// if the button state has changed:
if (reading2 != buttonState2) {
buttonState2 = reading2;
if (buttonState2 == HIGH) {
opponent = random(2,13);
Serial.print("opponent: ");
Serial.print(opponent);
Serial.println();
}
}
}
lastButtonState2 = reading2;
if ((millis() - lastDebounceTime1) > debounceDelay) {
// whatever the reading is at, it's been there for longer than the debounce
// delay, so take it as the actual current state:
// if the button state has changed:
if (reading1 != buttonState1) {
buttonState1 = reading1;
if (buttonState1 == HIGH) {
opponent = random(3,19);
Serial.print("opponent: ");
Serial.print(opponent);
Serial.println();
}
}
}
lastButtonState1 = reading1;
}
}
I placed your code in the wokwi simulator, see it here
I changed a number of things of no importance*. But I did replace all the random() calls with a call to a function that will properly roll N dice.
* I tipped your switches upside down for easier wiring with INPUT_PULLUP.
And I changed the logic a bit where you intend, I believe, to have an LED indication of "attack" vs. "opponent" input mode. I added an LED where it looked you you going to.
I have been losing to my GF consistently no matter I attack or oppose. Statring to feel like fun, room for improvments.
You debouncing and switch handling logic is separated and duplicative. As such, it is the proverbial "low hanging fruit" for making the code a bit smaller and easier to read, enhance and maintin.
// https://forum.arduino.cc/t/help-for-community-driven-masters-project/995395
const int buttonPin4 = 2; // the number of the pushbutton pin
const int buttonPin3 = 3;
const int buttonPin2 = 4;
const int buttonPin1 = 5;
const int ledPin = 8;
// Variables will change:
int buttonState4; // the current reading from the input pin
int lastButtonState4 = HIGH; // the previous reading from the input pin
int buttonState3;
int lastButtonState3 = HIGH;
int buttonState2;
int lastButtonState2 = HIGH;
int buttonState1;
int lastButtonState1 = HIGH;
// the following variables are unsigned longs because the time, measured in
// milliseconds, will quickly become a bigger number than can be stored in an int.
unsigned long debounceDelay = 50; // the debounce time; increase if the output flickers
unsigned long lastDebounceTime4 = 0; // the last time the output pin was toggled
unsigned long lastDebounceTime3 = 0;
unsigned long lastDebounceTime2 = 0;
unsigned long lastDebounceTime1 = 0;
int x = 0;
long attacker;
long opponent;
void setup() {
Serial.begin(9600);
Serial.println("Hello Community Driven Thing!\n");
pinMode(buttonPin4, INPUT_PULLUP);
pinMode(buttonPin3, INPUT_PULLUP);
pinMode(buttonPin2, INPUT_PULLUP);
pinMode(buttonPin1, INPUT_PULLUP);
pinMode(ledPin, OUTPUT);
lastButtonState4 = !digitalRead(buttonPin4); // the previous reading from the input pin
lastButtonState3 = !digitalRead(buttonPin3);
lastButtonState2 = !digitalRead(buttonPin2);
lastButtonState1 = !digitalRead(buttonPin1);
}
// roll ndice number of dice
unsigned char roll(unsigned char ndice)
{
unsigned total = 0;
for (unsigned char tt = 0; tt < ndice; tt++)
total += random(6) + 1;
return total;
}
void loop() {
// read the state of the switch into a local variable:
int reading4 = !digitalRead(buttonPin4);
int reading3 = !digitalRead(buttonPin3);
int reading2 = !digitalRead(buttonPin2);
int reading1 = !digitalRead(buttonPin1);
// check to see if you just pressed the button
// (i.e. the input went from LOW to HIGH), and you've waited long enough
// since the last press to ignore any noise:
// If the switch changed, due to noise or pressing:
if (reading4 != lastButtonState4) {
// reset the debouncing timer
lastDebounceTime4 = millis();
}
if (reading3 != lastButtonState3) {
// reset the debouncing timer
lastDebounceTime3 = millis();
}
if (reading2 != lastButtonState2) {
// reset the debouncing timer
lastDebounceTime2 = millis();
}
if (reading1 != lastButtonState1) {
// reset the debouncing timer
lastDebounceTime1 = millis();
}
if ((millis() - lastDebounceTime4) > debounceDelay) {
// whatever the reading is at, it's been there for longer than the debounce
// delay, so take it as the actual current state:
// if the button state has changed:
if (reading4 != buttonState4) {
buttonState4 = reading4;
// only toggle the LED if the new button state is HIGH
if (buttonState4 == HIGH) {
x++;
// moved up to before x is used / needed
if (x >= 2) {
x = 0;
}
// Serial.print(x);
if (x) {
digitalWrite(ledPin, HIGH);
// Serial.println(" so write HIGH");
}
else {
digitalWrite(ledPin, LOW);
// Serial.println(" so write LOW");
}
}
}
}
// save the reading. Next time through the loop, it'll be the lastButtonState:
lastButtonState4 = reading4;
if (x == 0) {
if ((millis() - lastDebounceTime3) > debounceDelay) {
// whatever the reading is at, it's been there for longer than the debounce
// delay, so take it as the actual current state:
// if the button state has changed:
if (reading3 != buttonState3) {
buttonState3 = reading3;
if (buttonState3 == HIGH) {
attacker = roll(1);
Serial.print("attacker: ");
Serial.print(attacker);
Serial.print(" Vs. ");
}
}
}
lastButtonState3 = reading3;
if ((millis() - lastDebounceTime2) > debounceDelay) {
// whatever the reading is at, it's been there for longer than the debounce
// delay, so take it as the actual current state:
// if the button state has changed:
if (reading2 != buttonState2) {
buttonState2 = reading2;
if (buttonState2 == HIGH) {
attacker = roll(2);
Serial.print("attacker: ");
Serial.print(attacker);
Serial.print(" Vs. ");
}
}
}
lastButtonState2 = reading2;
if ((millis() - lastDebounceTime1) > debounceDelay) {
// whatever the reading is at, it's been there for longer than the debounce
// delay, so take it as the actual current state:
// if the button state has changed:
if (reading1 != buttonState1) {
buttonState1 = reading1;
if (buttonState1 == HIGH) {
attacker = roll(3);
Serial.print("attacker: ");
Serial.print(attacker);
Serial.print(" Vs. ");
}
}
}
lastButtonState1 = reading1;
}
if (x == 1) {
if ((millis() - lastDebounceTime3) > debounceDelay) {
// whatever the reading is at, it's been there for longer than the debounce
// delay, so take it as the actual current state:
// if the button state has changed:
if (reading3 != buttonState3) {
buttonState3 = reading3;
if (buttonState3 == HIGH) {
opponent = roll(1);
Serial.print("opponent: ");
Serial.print(opponent);
Serial.println();
}
}
}
lastButtonState3 = reading3;
if ((millis() - lastDebounceTime2) > debounceDelay) {
// whatever the reading is at, it's been there for longer than the debounce
// delay, so take it as the actual current state:
// if the button state has changed:
if (reading2 != buttonState2) {
buttonState2 = reading2;
if (buttonState2 == HIGH) {
opponent = roll(2);
Serial.print("opponent: ");
Serial.print(opponent);
Serial.println();
}
}
}
lastButtonState2 = reading2;
if ((millis() - lastDebounceTime1) > debounceDelay) {
// whatever the reading is at, it's been there for longer than the debounce
// delay, so take it as the actual current state:
// if the button state has changed:
if (reading1 != buttonState1) {
buttonState1 = reading1;
if (buttonState1 == HIGH) {
opponent = roll(3);
Serial.print("opponent: ");
Serial.print(opponent);
Serial.println();
}
}
}
lastButtonState1 = reading1;
}
}
Now I must leave for that other place, going by bicycle this day - more dependable.