Matt, have you seen/played their Spacehack game? It looks fun. I suppose the game might quickly get repetitive, but for grabbing the public's attention at shows, that doesn't really matter very much.
I was talking to a couple of BIO'ers at the last meeting, and I thought about combining two ideas: something based on Theo Jansen's "Strandbeest" style walking robot (but a small portable size) but motorised and fitted with light following ability. There would be two light sensors (just LDRs) to allow the robot to steer towards a light source. That would grab attention at shows I think. The public could pick up a torch and the robot would walk towards them in that creepy, sinister crab-like way!
--
You received this message because you are subscribed to the Google Groups "BARNSLEY.IO" group.
To unsubscribe from this group and stop receiving emails from it, send an email to barnsleyio+...@googlegroups.com.
To post to this group, send email to barns...@googlegroups.com.
Visit this group at http://groups.google.com/group/barnsleyio.
To view this discussion on the web, visit https://groups.google.com/d/msgid/barnsleyio/76f5b11c-a2c8-4a98-a63b-a98b539138a3%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Hi guys,
As theres double doors from our room, through the building and to the outside, you do have a fair bit of room to play with. And with regards to storage, making it modular would help with that.
That big hak looks like a sinclair c5 on steroids lol. Good tho.
Chris
Hi paul,
Theres a chance i'll fetch mine. Mainly to help people with inkscape, but you can still use it for your sketch if need be.
Chris
All, I've made a start on the game sketch. Think I've got something that should work in my head and on paper. Maybe I will be ready to show something at the next meeting, though I may need to borrow someone's laptop to use the serial monitor to see the output.
Paul
--
You received this message because you are subscribed to the Google Groups "BARNSLEY.IO" group.
To unsubscribe from this group and stop receiving emails from it, send an email to barnsleyio+...@googlegroups.com.
To post to this group, send an email to barns...@googlegroups.com.
Visit this group at http://groups.google.com/group/barnsleyio.
To view this discussion on the web, visit https://groups.google.com/d/msgid/barnsleyio/83ea82cf-03b0-4403-b75b-a8b288e9137e%40googlegroups.com.
Well I won't be able to make the next meet up. A slight calendar miscalculation led me to believe I would be able to but I can't.
I've been looking into the programming side of it myself and looking at how the algorithms are formed for the computer to play the game. It seems an Atmel/Arduino should be capable of running the routines, not sure about the performance of it until we give it a try.
// Connect 4
// P Beard
// Jan 2015
// Used as array of bits for positions of "X" tokens
unsigned long long boardO;
// Used as array of bits for positions of "O" tokens
unsigned long long boardX;
// Array containing start position of each "win line"
byte lineStart[] = { 0, 7, 14, 21, 28, 35, 0, 1, 2, 3, 4, 5, 6, 14, 7, 0, 1, 2, 3, 3, 4, 5, 6, 13, 20};
// Array containing number of groups of 4 positions on each "win line"
byte lineReps[] = { 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 1, 2, 3, 3, 2, 1, 1, 2, 3, 3, 2, 1};
// Array containing direction offset for each "win line"
byte lineSteps[] = { 1, 1, 1, 1, 1, 1, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 6, 6, 6, 6, 6, 6};
void setup() {
Serial.begin(9600);
Serial.println();
Serial.println("~~~~~~Connect 4~~~~~~");
Serial.println();
displayBoard(boardO, boardX);
}
void loop() {
char c;
byte best;
int currentScore;
Serial.println();
Serial.print("Your go:");
while (Serial.available() < 1) {} // wait for key code
c = Serial.read();
Serial.println(c);
Serial.println();
while (Serial.available() < 1) {} // wait for CR code
Serial.read(); // discard CR code
dropCounter(boardX, boardO, c - '0');
displayBoard(boardO, boardX);
if (score(boardO, boardX) < -500) {
Serial.println();
Serial.println ("****CONGRATULATIONS YOU WIN****");
Serial.println();
Serial.println("Press reset for new game");
while (true) {}
}
else {
Serial.println();
Serial.print("My go:");
currentScore = bestMove(boardO, boardX, best, 3);
Serial.println(best);
Serial.println();
dropCounter(boardO, boardX, best);
displayBoard(boardO, boardX);
if (score(boardO, boardX) > 500) {
Serial.println();
Serial.println ("****COMISERATIONS YOU LOST****");
Serial.println();
Serial.println("Press reset for new game");
while (true) {}
}
}
}
void displayBoard(unsigned long long boardO, unsigned long long boardX) {
unsigned long long mask = 1ULL;
for(byte col = 0; col <= 6; col++) Serial.print(col);
Serial.println();
for(byte row = 0; row <= 5; row++) {
for(byte col = 0; col <= 6; col++) {
if (boardO & mask) Serial.print('O');
else if (boardX & mask) Serial.print('X');
else Serial.print('.');
mask = mask << 1;
}
Serial.println();
}
}
void dropCounter(unsigned long long &boardO, unsigned long long &boardX, byte col) {
unsigned long long mask = 1ULL << col;
byte row = 0;
while (row < 5) {
// Test if the position one row down is empty
unsigned long long mask2 = mask << 7;
if (((boardO | boardX) & mask2)) break;
// Move down a row
row++;
mask = mask2;
}
// Place the token here
boardO |= mask;
}
int score(unsigned long long &boardO, unsigned long long &boardX) {
int totalScore = 0;
// Scan each "win" line on the board
for (byte line = 0; line < 25; line++) {
unsigned long long mask = 1ULL << lineStart[line];
byte lineStep = lineSteps[line];
// Scan each group of 4 positions on the "win" line
for (byte rep = 0; rep < lineReps[line]; rep++) {
unsigned long long mask2 = mask;
int addScore = 0;
// Check this group of 4 positions
for (byte pos = 0; pos < 4; pos++) {
if (boardO & mask2) {
if (addScore < 0) {
// A mix of "O" and "X" - no-one can win here
addScore = 0;
break;
}
else if (addScore == 0) addScore = 1;
else if (addScore == 1) addScore = 10;
else if (addScore == 10) addScore = 100;
else if (addScore == 100) addScore = 1000;
}
if (boardX & mask2) {
if (addScore > 0) {
// A mix of "O" and "X" - no-one can win here
addScore = 0;
break;
}
else if (addScore == 0) addScore = -1;
else if (addScore == -1) addScore = -10;
else if (addScore == -10) addScore = -100;
else if (addScore == -100) addScore = -1000;
}
// Move to next position in group of 4
mask2 = mask2 << lineStep;
}
// Move to next start position of next group-of-4 on the win line
mask = mask << lineStep;
// Add any score to the total score for the board
totalScore += addScore;
}
}
return totalScore;
}
int bestMove(unsigned long long &boardO, unsigned long long &boardX, byte &best, byte lookAhead) {
int bestScore = -9999;
byte best2;
// Evaluate each choice of column
for (byte col = 0; col < 7; col++) {
// Make a copy of the board
unsigned long long boardOcopy = boardO;
unsigned long long boardXcopy = boardX;
int thisScore;
// Drop token onto the copy of the board
dropCounter(boardOcopy, boardXcopy, col);
if (lookAhead == 0)
// Don't look any more moves ahead, just evaluate the board as-is
thisScore = score(boardOcopy, boardXcopy);
else
// Look more moves ahead, but swap places with opponent since it will be their turn next
thisScore = -bestMove(boardXcopy, boardOcopy, best2, lookAhead - 1);
// Check this choice of column - best so far?
if (thisScore > bestScore) {
// Yes, a new best choice found
bestScore = thisScore;
best = col;
}
}
return bestScore;
}
// Connect 4
// P Beard
// Jan 2015
char board[43];
// Array containing start position of each "win line"
byte lineStart[] = { 0, 7, 14, 21, 28, 35, 0, 1, 2, 3, 4, 5, 6, 14, 7, 0, 1, 2, 3, 3, 4, 5, 6, 13, 20};
// Array containing number of groups of 4 positions on each "win line"
byte lineReps[] = { 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 1, 2, 3, 3, 2, 1, 1, 2, 3, 3, 2, 1};
// Array containing direction offset for each "win line"
byte lineSteps[] = { 1, 1, 1, 1, 1, 1, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 6, 6, 6, 6, 6, 6};
void setup() {
Serial.begin(9600);
Serial.println();
Serial.println("~~~~~~Connect 4~~~~~~");
Serial.println();
for (byte i = 0; i < 43; i++) board[i] = '.';
displayBoard(board);
}
void loop() {
char c;
byte best;
int currentScore;
Serial.println();
Serial.print("Your go:");
while (Serial.available() < 1) {} // wait for key code
c = Serial.read();
Serial.println(c);
Serial.println();
while (Serial.available() < 1) {} // wait for CR code
Serial.read(); // discard CR code
dropCounter(board, 'O', c - '0');
displayBoard(board);
if (score(board, 'X', 'O') < -500) {
Serial.println();
Serial.println ("****CONGRATULATIONS YOU WIN**** ");
Serial.println();
Serial.println("Press reset for new game");
while (true) {}
}
else {
Serial.println();
Serial.print("My go:");
currentScore = bestMove(board, 'X', 'O', best, 4);
Serial.println(best);
Serial.println();
dropCounter(board, 'X', best);
displayBoard(board);
if (score(board, 'X', 'O') > 500) {
Serial.println();
Serial.println ("****COMISERATIONS YOU LOST****");
Serial.println();
Serial.println("Press reset for new game");
while (true) {}
}
}
}
void displayBoard(char board[]) {
Serial.println("0123456");
byte i = 0;
for(byte row = 0; row <= 5; row++) {
for(byte col = 0; col <= 6; col++) {
Serial.print(board[i++]);
}
Serial.println();
}
}
void dropCounter(char board[], char token, byte col) {
byte i = col;
// Test if the position one row down is empty
while (i < 35 && board[i+7] == '.') {
// Move down a row
i += 7;
}
// Place the token here
board[i] = token;
}
int score(char board[], char token, char oppToken) {
int totalScore = 0;
// Scan each "win" line on the board
for (byte line = 0; line < 25; line++) {
byte i = lineStart[line];
byte lineStep = lineSteps[line];
// Scan each group of 4 positions on the "win" line
for (byte rep = 0; rep < lineReps[line]; rep++) {
byte j = i;
int addScore = 0;
// Check this group of 4 positions
for (byte pos = 0; pos < 4; pos++) {
if (board[j] == token) {
if (addScore < 0) {
// A mix of "O" and "X" - no-one can win here
addScore = 0;
break;
}
else if (addScore == 0) addScore = 1;
else if (addScore == 1) addScore = 10;
else if (addScore == 10) addScore = 100;
else if (addScore == 100) addScore = 1000;
}
if (board[j] == oppToken) {
if (addScore > 0) {
// A mix of "O" and "X" - no-one can win here
addScore = 0;
break;
}
else if (addScore == 0) addScore = -1;
else if (addScore == -1) addScore = -10;
else if (addScore == -10) addScore = -100;
else if (addScore == -100) addScore = -1000;
}
// Move to next position in group of 4
j += lineStep;
}
// Move to next start position of next group-of-4 on the win line
i += lineStep;
// Add any score to the total score for the board
totalScore += addScore;
}
}
return totalScore;
}
int bestMove(char board[], char token, char oppToken, byte &best, byte lookAhead) {
int bestScore = -9999;
byte best2;
// Evaluate each choice of column
for (byte col = 0; col < 7; col++) {
int thisScore;
// Make a copy of the board
char boardCopy[43];
for (byte i = 0; i < 43; i++) boardCopy[i] = board[i];
// Drop token onto the copy of the board
dropCounter(boardCopy, token, col);
if (lookAhead == 0)
// Don't look any more moves ahead, just evaluate the board as-is
thisScore = score(boardCopy, token, oppToken);
else {
// Look more moves ahead, but swap places with opponent since it will be their turn next
thisScore = -bestMove(boardCopy, oppToken, token, best2, lookAhead - 1);I done a drawing. Titter ye not.

Actually...as I reread my post just then I noticed the obvious answer...lol.
I've been looking if it's possible to swap those else ifs to a switch but it's having trouble with it. Also a few other changes. It's very almost working and will be uploaded as soon as it is.
My version basically is a PC version that can be used for testing or also in case we decide to swap the controller over to a pi for example.
Also I am trying to cut down the score and bestmove functions as they are both used in the recursive look ahead part so trying to get them as lean as possible would speed up the turns.
Once that's done I will see if we can't knock together some sort of movement functions.
> --
> You received this message because you are subscribed to a topic in the Google Groups "BARNSLEY.IO" group.
> To unsubscribe from this topic, visit https://groups.google.com/d/topic/barnsleyio/kiFtN-1KCYk/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to barnsleyio+...@googlegroups.com.
> To post to this group, send email to barns...@googlegroups.com.
> Visit this group at http://groups.google.com/group/barnsleyio.
> To view this discussion on the web, visit https://groups.google.com/d/msgid/barnsleyio/28436bd7-5f93-4b89-96c3-d6eb867c212a%40googlegroups.com.
Thoughts?
const byte btnRow[2] = {2, 3};
const byte btnCol[7] = {4, 5, 6, 7, 8, 9, 10};
volatile byte rowTrig = 0;
volatile byte colTrig = 0;
void setup() {
Serial.begin(57600);
pinMode(btnRow[0], INPUT_PULLUP);
pinMode(btnRow[1], INPUT_PULLUP);
for (byte col=0; col<=6; col++) {
pinMode(btnCol[col], OUTPUT);
digitalWrite(btnCol[col], LOW);
}
attachInterrupt(0, readBtnRow0, FALLING);
attachInterrupt(1, readBtnRow1, FALLING);
}
void readBtnRow0() {
readBtn(0);
}
void readBtnRow1() {
readBtn(1);
}
void readBtn(byte row) {
for (byte col=0; col<=6; col++) {
pinMode(btnCol[col], INPUT);
if (digitalRead(btnRow[row]) == HIGH) {
rowTrig = row+1;
colTrig = col+1;
}
pinMode(btnCol[col], OUTPUT);
digitalWrite(btnCol[col], LOW);
}
}
void loop(){
if (colTrig != 0 || rowTrig != 0) {
Serial.print("Row ");
Serial.print(rowTrig);
Serial.print(" Col ");
Serial.println(colTrig);
rowTrig = 0;
colTrig = 0;
}
}
Issue is in readBtn.
If btnRow[row] is high then colTrig will always be 7 as the loop ends when col is 6 and colTrig is always col+1
> --
> You received this message because you are subscribed to a topic in the Google Groups "BARNSLEY.IO" group.
> To unsubscribe from this topic, visit https://groups.google.com/d/topic/barnsleyio/kiFtN-1KCYk/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to barnsleyio+...@googlegroups.com.
> To post to this group, send email to barns...@googlegroups.com.
> Visit this group at http://groups.google.com/group/barnsleyio.
> To view this discussion on the web, visit https://groups.google.com/d/msgid/barnsleyio/15308072-19c7-4779-8501-1205df7e1251%40googlegroups.com.
Issue is in readBtn.
If btnRow[row] is high then colTrig will always be 7 as the loop ends when col is 6 and colTrig is always col+1
Then could the issue be speed? There are no delays in the loop and so there is no de-bounce mechanism so it's possible that mechanical bounce or even line capacitance are stopping the pin state from changing fast enough for the loop to detect it.
Alternatively there is a known issue in the data sheets when changing the pin data directions when using those same pins as outputs or pull ups. An intermediate step must be used to drop the output low/pull up off between the mode change otherwise the behaviour of the pin is undefined.
I'm not sure if the arduiono pinMode function accounts for that or not. However adding a small delay into the loop should solve both problems.
Let's hope that solves the issue at hand.
after the pinMode(btnCol[col], INPUT); but it made no difference.
(Can't see how a delay could fix that data sheet issue you mentioned though.)
I will post on Arduino forum, there are some good trouble-shooters on there. Perhaps they can give us some ideas to try.
A Delay in the right place would allow the pin to return to a defined state before it is read.
It's also with pointing out that you can read a pin regardless of its "pinMode" so changing each pin to input then back to output to check it is not needed.
Simply leave the col pins set as an output and change their state while reading the row pins.
So have the col pins all low to begin with as you already have, but then once the interrupt triggers turn them all high one by one and check which one makes the row pin go back high = you just found which switch is closed.
Then drop all the col pins back low to get ready for the next interrupt.
Thanks again Kevin, its a theory. I tried adding delayMicroseconds(20000); after the pinMode(btnCol[col], INPUT); but it made no difference.
(Can't see how a delay could fix that data sheet issue you mentioned though.)
I will post on Arduino forum, there are some good trouble-shooters on there. Perhaps they can give us some ideas to try.
--
You received this message because you are subscribed to a topic in the Google Groups "BARNSLEY.IO" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/barnsleyio/kiFtN-1KCYk/unsubscribe.
To unsubscribe from this group and all its topics, send an email to barnsleyio+...@googlegroups.com.
To post to this group, send email to barns...@googlegroups.com.
Visit this group at http://groups.google.com/group/barnsleyio.
To view this discussion on the web, visit https://groups.google.com/d/msgid/barnsleyio/7f6e30dd-c250-4773-bd42-154ed852ed4b%40googlegroups.com.
I had made a similar thing but with just one row of 8 buttons It also used a shift register instead of the Arduinos pins for your 'col'. The code did use pull ups on the interrupt pin but didn't need to tri-state the other inputs (tri-stating a 74595 is only possible when the whole output register is disabled) and I've only just remembered now how mine didn't have the short circuit issue which is why your code was confusing me a little.
To prevent a short circuit I used an LED on each of the 'col' pins facing into the register, in your case into your col pins.
The diodes allow current from the pull up row pins to sink into the low level input through the closed switch and trigger the interrupt.
The interrupt service then puts the col pins high while checking the row pin. The diodes stop the strong source current from the col pins flowing back into another pin in case two switches are closed. When the pin with the switch closed is high that pin cannot sink current and the row pin will read high.
So the addition of 7 LEDs (or normal diodes if you don't want them to flicker as switches are closed) would allow for simpler code.
It should also be possible to have both interrupts ISRs call the same function that checks both rows. That would allow for the simultaneous operation of both rows of buttons to be dealt with.
At the moment if INT0 is triggered then INT1 would be ignored if it happened while the first interrupt is still executing.
This would then output both row values each time ether or both are pressed.
Debouncing is also still needed by adding a small delay (1ms maybe) between the ISR call and the readbuttons call this will make sure the button is fully closed and not still bouncing while the read is in progress and also a tiny delay between each pin read (1-5 microseconds) to make sure line capacitance isn't an issue if you're using long wires around the frame.
Additionally the ISR needs to reset the initial pin states and wait until the button is released again, then delay (1ms) again to make sure the the ISR isn't triggered again by any bounce on the other side.