Group Project?

95 views
Skip to first unread message

Paul Beard

unread,
Jan 7, 2015, 5:29:23 PM1/7/15
to barns...@googlegroups.com
How about a project to get as many people as possible from the group engaged and working together on something? Ideally it should require a variety of different skills and abilities so that everyone has something to contribute and something new to learn. A bit of engineering, a bit of artistry, a bit of electronics and a bit of programming? Please add your ideas to this thread.

Matt Brailsford

unread,
Jan 8, 2015, 3:21:49 AM1/8/15
to barns...@googlegroups.com
Great idea Paul,

I couple of things I'd add is if it could be something interactive, and easily portable as it would be great if this could be a show piece for the group that we take to events and spark interest in the public.

A good example would be the York Hackspaces Spacehack http://spacehack.york.hackspace.org.uk/ which was their group project.

I look forward to seeing what people think.

Matt

Paul Beard

unread,
Jan 10, 2015, 12:46:56 PM1/10/15
to barns...@googlegroups.com
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!


Paul Beard

unread,
Jan 12, 2015, 3:36:40 AM1/12/15
to barns...@googlegroups.com
My idea has been done before:

http://youtu.be/MXp7Og7O7N0

Matt Brailsford

unread,
Jan 12, 2015, 3:51:09 AM1/12/15
to barns...@googlegroups.com
Hey Paul,

I haven't play'd the spacehack project myself, but from what I remember, they can pre-program different missions into it, so whilst yes, you can only perform the same actions (push buttons, flip switches etc) they can change the mission and thus have a completely different game to play. I believe they may be working on another similar game for bomb disposal where people work in split teams, one half does the "wire cutting" where as the other half, from a set of rules, figures out the right instructions to dismantle the bomb and relays them to team 1.

I know storage is a bit of an issue at horizon, but personally, I'd quite like to see more of a large scale project. Something to impress and that pulls you in from a far. Another good example that springs to mind is the bighak project.


Just some food for thought.

Matt

On 10 January 2015 at 17:46, Paul Beard <paul.r...@googlemail.com> wrote:
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.

chris stagg

unread,
Jan 13, 2015, 1:38:58 AM1/13/15
to barns...@googlegroups.com

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

kal9001

unread,
Jan 14, 2015, 4:50:52 PM1/14/15
to barns...@googlegroups.com
I love the look of that Spacehack thing I just want to jump through the screen and have a go!. Something like that or very similar gets my vote right away.

So from what I gather from the discussion so far is that were talking about a crowd pleaser, Something fun to show off rather than something practical or technical.
Just need to get my brain in gear and see what it spits out in a day or two.

The good news is that it looks like I can make the next meetup as I am off, I am hopeful that the impending rota change at work may shuffle me onto the other shift or that maybe the short month in Feb could resync my 'early' weeks with the B:IO days again...Otherwise I may need to hatch a cunning plan as it will not be fun missing 5/7 of the meetups on average.

Bob Stone

unread,
Jan 20, 2015, 5:36:01 PM1/20/15
to barns...@googlegroups.com
Hi guys,

Some SpaceHack videos: 
* Mitch Altman and Jimmie P Rodgers at UK Maker Faire 2014: http://youtu.be/meE0VHRasLk
* More players at UK Maker Faire 2014: http://youtu.be/oCWH3n4aLJI
* Rebranded as the Infinite Improbability Drive for Towel Day 2014: http://youtu.be/clKC7ZtxFV0
* Fun Palaces at the West Yorkshire Playhouse: http://youtu.be/vpMHhVT8L74
(we took it to around ten events in 2014 including three Maker Faires)

We were specifically going for:
* Interactive - actively engage the public rather than have them passively walk by a trestle table jumble sale of stuff we made
* Intuitive - and if anything we hoped to spend less time explaining the game than has turned out to be the case (kids 12+ got it very quickly though)
* One single theme so there is only one thing to remember us for - actually lots of small projects contribute to the overall theme of SpaceHack outside the game, but all done in theme
* Multidisciplinary - it was something we could assign tasks to people for graphic design, 2D laser cut design, 3D printing CAD design, Python programming, circuitry, woodwork etc
* Modular - we only have our space one day a week and can't leave big projects there, so we had no option but to make something we could drive to and from the space in separate manageable bits

Matt's got a couple of great pieces based on Pixar stuff made real, so that's a candidate theme you might want to expand upon, for instance.

Bob :)

Paul Beard

unread,
Jan 20, 2015, 6:52:48 PM1/20/15
to barns...@googlegroups.com
Hi all, I will write up our connect 4 ideas in more detail later, but for now here's something I found on youtube:

http://youtu.be/xYrOqAtKG8w

Paul Beard

unread,
Jan 20, 2015, 6:59:52 PM1/20/15
to barns...@googlegroups.com

m...@mattbrailsford.com

unread,
Jan 21, 2015, 9:39:09 AM1/21/15
to barns...@googlegroups.com
Great chat with everyone last night and really exited to see where this project goes. It sounds like we have struck a similar balance to what @bob mentioned in that it's a simple idea for people to approach and won't take much explaining (It's connect 4) but can equally be broken down into different disciplines so plenty of opportunities to learn something new.

@bob thanks for the insite into the space hack project, it's really good to see the inspiration behind the project and as you can tell, I am a big fan :) It's also great to see we have very much a similar ethos behind it as we are in a similar boat given that we are new group so need to entice people to come chat with us, but are equally restricted with the space. SpaceHack is definitely a great inspiration to show that it is possible within such a group though.

Lets get this party started :)

Matt


Paul Beard

unread,
Jan 21, 2015, 3:00:15 PM1/21/15
to barns...@googlegroups.com
OK, let me describe one of the ideas we discussed, as best as I can remember.

The idea was a giant Connect-4 game. That is, as "giant" as possible while being portable enough to take to maker fairs etc., and not so big that a 7-ish year old could not reach to put their counter in the slot at the top.

For anyone unfamiliar with Connect-4, two players take turns dropping their coloured counter into a vertical frame in an attempt to get 4 of their counters in a row (vertically, horizontally or diagonally) while blocking their opponent from doing the same. Like noughts and crosses on a 6 x 7 grid, except that gravity determines which row a counter lands in depending how many counters are already present in the player's chosen column.

The frame could be made from wood/plywood and the counters from wood also, to make them "chunky" and the whole thing could be painted colourfully and adorned with leds and sound effects to attract attention. Another suggestion was to give it a Steampunk style with for example polished wood & brass but combined with contrasting and obviously non-Victorian/Edwardian hi-tech components.

Human players could take on the machine. In the first and most basic version, all counters would all be handled by the human, with the machine indicating its desired move in some simple way, for example LEDs. Sensors or switches at the top of each slot would enable the machine to detect which column the human has dropped their counter into. The machine, possibly Arduino, would calculate its move and use the LEDs to indicate its chosen column.

The level of difficulty for the human, in other words how cleverly the machine plays, could be adjusted by one of us, remotely or secretly, to suit the human opponent playing at the time.

In the next level of complexity, the machine would be able to place its own counters into the frame in some way. Possible solutions could include a robot arm or something gravity driven with slots for the counters to roll down and trap doors or other mechanisms to direct the counter into a particular column. A human would have to empty the frame, sort the counters and load the machine's counters into a dispenser of some kind at the start of each game.

Getting more complex still, the machine could be able to empty itself, sort the counters and prepare for the next game. It could even play itself to attract attention when no-one was playing.

Other ideas included a setting where, if the machine believed it was about to be beaten by its human opponent, it would cheat in some way, for example displaying annoyance at the time taken by its opponent to make a move, moving one of its opponent's counters or preventing them from playing their last move and taking a second turn itself. This would be done in some clearly humorous way and we would then apologise for the bugs in the machine and wish the opponent better "luck" next time.

Please add any ideas I missed - there were a lot!

Paul

kal9001 .

unread,
Jan 21, 2015, 6:46:18 PM1/21/15
to barns...@googlegroups.com
After some thought on this I have formulated my own personal short list of options which I will submit for your consideration.
  • The two hoppers would be static and store the counters horizontally (like stacked plates) and would be able to store enough counters for the whole game (21 each). Ether hopper must be able to dispense and reload a counter ether from or to the carriage or a person.
  • The carriage would move along a 'track' of some kind. Capable of unloading a counter from ether hopper and reorientating it to a vertical position while it navigates itself to the column the controller wishes to use.
  • The carriage track would be set above and behind top of the frame to allow the human player to easily and safely take their turn so the carriage would need a mechanism to move the counter and holding mechanism over the hole properly.
  • A simple mechanism to raise all the counters partially out of the frame row by row to be collected by the same carriage and placed ether back in ether hopper. Although this sounds slow it need not be with a properly designed carriage and pick-up system.
  • The carriage has colour sensing or uses the frames data on the controller to know what colour it is picking up, It is then able to reorientate the counter to be loaded into ether hopper. this would facilitate the speedy reset as it would be able to put any counter in any hopper only by flipping them over.

    • This also allows the user to pick ether colour they want to play without needing two full sets of counters or having to change hoppers.
    • This can also facilitate the 'cheating' behaviour as it can flip the counter over at the last moment to change its colour.

  • The final version would be themed some how.

    • Steampunk: Perhaps a pneumatic cylinder or even a real stream or compressed air engine can drive the carriage. plus chrome and brass fittings and embellishments along with old style light bulbs for status signals and a 'fake' mechanical computer complete with intricate gears which randomly click reed switches, big clunky buttons and vavle type knobs.

    • rat-look: The thing would be made to look like it just randomly fell together in a scrapyard. Everything would be made to look like it came from something else and look like it could all fall to bits at any moment. Everything rattles and shakes, The mechanical operation would seem extremely clumsy and hesitant although it would be all solid engineering behind it.

    • Modern: Ultra modern, 'Apple' look, Sleek and everything very well ordered and symmetric, with quiet and precise operation.

    • B-movie: 1960/70s B-movie inspired, fairly standard mechanical design but with lots of buttons and random lights to make it look like a 1960s mainframe computer. A Jacobs latter and "beep, boop" sort of noises.

I understand that initially we just need a working prototype. It seems clear the there are four main areas of this project;
The software/AI that can track a two player game also compute a strategy to play a person or itself in single or zero player mode.
The controller software that handles the actual sensors and movement of the carriage in game and in the reset phase.
The physical build of the frame and carriage system and the electronics involved in making it all work.
Finally, the beautification and theme of the game.

The easiest thing to get stuck into is the software and AI, As we discussed at the meeting having an Arduino or Raspberry pi able to render a simple grid display to play a purely software version of the game to a competent standard. This can be done independently of the other steps.
We also need to try and agree on a general theme for this as that may be needed to influence the physical build which in turn may affect the controller and hardware interface (for example, motors or solenoids for the picker, A steam engine or a stepper motor or a DC motor for the drive, Reed, leaf, hall effect or optical switches for the columns?)

At this stage we should have a vague physical design emerging with some dimensions and specs for parts we can start getting and making. It may be possible to make a quick mock up that works on a small retail connect four game which can be used to debug the control, game and AI software and also provide some sort of tangible product to keep us all motivated and to take to shows while the big thing is still being made.

I know this is a long post but I hope everything I said makes sense, Although the actual design is debatable the process to follow I think is sound, So please get posting your opinions on how you think it could be themed, how you think it should work...And once everything is in the pot and we give it a good stir we should get something tasty to begin constructing.

m...@mattbrailsford.com

unread,
Jan 22, 2015, 4:50:12 AM1/22/15
to barns...@googlegroups.com
Personally looks wise, I'd probably want it to look like the original game blown up, that way it's instantly recognizable to people. That said I guess this is really the least important thing right now and the physical build may need to dictate this so I'd say focus on the build and sort this when it makes most sense to do so.

I think in order to promote productivity it would be a good idea to create a list of features that will form "phase 1" and then break that down into tasks that need to be performed so that they can be assigned to people.

With that in mind, I've put together a simple spreadsheet that everyone can access to add features to. I've setup tabs along the bottom to manage phases so we can focus on what is most important for now and not get distracted by features not needed yet.


Matt


Paul Beard

unread,
Jan 25, 2015, 8:04:52 AM1/25/15
to barns...@googlegroups.com
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

chris stagg

unread,
Jan 25, 2015, 10:28:13 AM1/25/15
to barns...@googlegroups.com

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

On 25 Jan 2015 13:04, "Paul Beard" <paul.r...@googlemail.com> wrote:
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.

kal9001 .

unread,
Jan 26, 2015, 11:13:35 AM1/26/15
to barns...@googlegroups.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.

Paul Beard

unread,
Jan 27, 2015, 11:41:44 AM1/27/15
to barns...@googlegroups.com
I'm making good progress on the connect 4 sketch. It is looking 3 moves ahead, which is good enough to beat me already (not that I'm any good at the game!)

At this level its taking around 6~7 seconds per move, but I have some ideas that might speed that up to a degree. I'll try them out tonight and see how far that gets me. Then I'll post the sketch here for you guys to test out, review code (I'd better put a few comments in!) and suggest improvements.

Would be good to get to 4 moves ahead, as long as that doesn't take more than 15~20 secs (or run out of ram!). At the monent the algorithm is a bit crude and every extra move it looks ahead takes 7 times longer.

Paul Beard

unread,
Jan 27, 2015, 6:41:20 PM1/27/15
to barns...@googlegroups.com
Well, my "improvements" made no difference...

Here is the sketch for anyone who wants to try it out. Any suggestions welcome!

// 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;
}






Paul Beard

unread,
Jan 29, 2015, 6:35:56 PM1/29/15
to barns...@googlegroups.com
I've had second thoughts about the way I implemented that sketch.

I was originally concerned that the limiting factor might be memory (ram), so I used two "unsigned long long" (64-bit integers) to hold the positions of each player's counters (only using 42 of each 64 bits). This would have the advantage of taking up only 16 bytes for each copy of the board, and many copies would have to be made to allow the sketch to look ahead several moves. I was imagining that 7 copies would be needed to look ahead each move, so 49 copies for two moves ahead, for example. The downside would be the slow speed of manipulating the 64-bit integers as though they were arrays: shifting, masking etc.

But once I had it working, I realised my error. Each extra move that the sketch looks ahead only requires one more copy of the board. So memory is not a problem, even if I used a conventional 42-byte array. And a conventional array is much faster at indexing any given location on the board, compared to all that shifting & masking.

So here is version 2. Looking ahead 3 moves now only takes 1 second. Looking ahead 4 moves now takes a much more acceptable 7 seconds.

The way the sketch detects a win, by either player, is not yet perfect. Sometimes it detects a win when it has not happened, and potentially vice-versa. It's because of those arbitrary "500" thresholds. I need to find a better way! Anyone got an idea?

// 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);

kal9001 .

unread,
Jan 29, 2015, 7:54:48 PM1/29/15
to barns...@googlegroups.com
In my own notably very incomplete version I use a 6x7 two-dimensional array to store the board, It is an array of chars with two bits representing, Blank, player one and player two, The other six bits I intend to use as various flags for the AI to play, Thinks like key positions to block or avoid, But I'm not sure how that will work out.

Testing I am doing at the moment is in a linux terminal so the output is somewhat different, However once its working it should copy reasonably easily into ether an Arduino sketch or Atmel studio to go onto a microcontroller.

I decided to 'roll my own' as the saying goes as this may give some alternate ideas for how to do things, But also a personal exercise. Once I get some more of it working I will post it on here so you can have a look perhaps.
Its almost at the stage where it can play a game with two people and hopefully detect wins.
Next step will be to have it calculating the best moves to give 'hints' to players. which should then lead right into a full blown AI to play the game.

Paul Beard

unread,
Jan 31, 2015, 2:16:52 PM1/31/15
to barns...@googlegroups.com


I done a drawing. Titter ye not.



Paul Beard

unread,
Feb 1, 2015, 4:23:03 AM2/1/15
to barns...@googlegroups.com
Sorry that image is so poor. The original was scanned at 300dpi, but google groups shrank it. I will bring the original on Tue plus an Arduino with the connect 4 sketch loaded but I dont have a laptop and will need to use serial monitor.

Anyone had any thoughts about detecting tokens dropped into the columns? Microswitches, photodiodes/phototransistors/ldrs? With illuminators?

kal9001

unread,
Feb 3, 2015, 4:06:49 AM2/3/15
to barns...@googlegroups.com
I guess the easiest way is to use a mechanical switch. You can get them with extremely light actions, then ether a thin piece of wire (paperclip maybe) would protrude from the switch and into each column.
token goes in and pushes the wire aside which moves the switch.
the action of the switch would be strong enough to push the paperclip back but not to impede the token.

The same could be done with optical switches but the paperclip would have some black tape on the end and would ether be balanced or sprung so it always 'springs Jack's.

Paul Beard

unread,
Feb 4, 2015, 5:48:05 PM2/4/15
to barns...@googlegroups.com
I have attempted to fix the sketch after the bugs we discovered at the Old No 7 last night. I have uploaded the latest version to GitHub.

Everyone please feel free to give it another test.

https://github.com/barnsleyio/connect4/blob/master/connect4.ino

You don't need a board to play, just use the Serial Monitor @9600

kal9001

unread,
Feb 19, 2015, 9:47:03 PM2/19/15
to barns...@googlegroups.com
I am confused a little by this code at the moment, I seem to be understanding how it works apart from one particular issue

In the score function it says.

      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) {
            // The game has been won
            totalScore = 1000;
            goto gameWon;
          }
        }

this issue is that you give addScore a value of zero and then test it right away. To my eyes this test, And the one right after that checks for if(addScore > 0) will both return false as addScore is always set to zero right before the comparison.

kal9001 .

unread,
Feb 19, 2015, 10:05:29 PM2/19/15
to barns...@googlegroups.com

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.

> To view this discussion on the web, visit https://groups.google.com/d/msgid/barnsleyio/28436bd7-5f93-4b89-96c3-d6eb867c212a%40googlegroups.com.

Paul Beard

unread,
Apr 25, 2015, 1:38:25 PM4/25/15
to barns...@googlegroups.com

All,

I have attempted to come up with a circuit to detect the tokens getting dropped into the connect-4 board.

If you remember, the idea was to have two switches, side-by-side, inside each of the 7 columns, near the top of each column, to detect a token posted into that column. The two different token colours would also have 2 different edge-shapes. One colour would active both switches as it passes them. The other colour would activate only one switch. By detecting which switches are triggered, the Arduino sketch can tell which colour token has been dropped into which column.

Because a cheating opponent might drop a token in when it is not their turn, and the Arduino is busy considering its next move, it would be a good idea if the buttons triggered an interrupt when they are activated. This means the Arduino does not need to continually check for button presses. When a button press occurs, it is detected on one of the Arduino's special interrupt pins. Whatever the sketch is currently doing is suspended and a special piece of code gets run to record the button press. The sketch can then be resumed where it left off.

Unfortunately we have 14 buttons and only 2 special interrupt pins! Hence my circuit. The idea is that it would operate as follows:

  1. Initially, pins 2 and 3 (the special interrupt pins) are set as inputs with pullup. (Pullup means that they read HIGH unless the pin is connected to ground, when it reads LOW. This prevents them from "floating"). Pins 4 to 10 are set as outputs and set to LOW.
  2. When any one of the odd-numbered the buttons is pressed (see diagram), pin 3 is pulled low. If an even-numbered switch is pressed, pin 2 is pulled low. Either way, an interrupt is "fired".
  3. The interrupt code immediately knows if it was an odd or even numbered switch, based on which pin caused the interrupt.
  4. The interrupt code then has to determine which of the 7 possible switches (odd or even) was pressed. To do this, it goes through pins 4 to 10 in turn, setting that pin to HIGH. If pin 2 or 3 then also reads HIGH, it must have been the switch connected to that pin that is pressed. (Pin 4 corresponds to the leftmost column on the board and pin 10 to the rightmost column.)

Thoughts?

Paul Beard

unread,
Apr 30, 2015, 5:57:19 PM4/30/15
to barns...@googlegroups.com


All,

Having trouble with the sketch to read these buttons. The sketch correctly identifies the row as 1 or 2, but reports the column as 7 every time. Can anyone spot why?

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;
 
}
}
 
   
     



kal9001 .

unread,
May 1, 2015, 5:25:10 AM5/1/15
to barns...@googlegroups.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

> --
> 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.

Paul Beard

unread,
May 1, 2015, 6:29:26 AM5/1/15
to barns...@googlegroups.com


On Friday, 1 May 2015 10:25:10 UTC+1, kal9001 wrote:

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

Thanks for looking Kevin. But btnRow[row] should not be high for the whole loop.

When the function is called, btnRow[row] is being pulled low by one of the 7 btnCol[] pins, because one of the switches is closed. We know that is the case because the interrupt was generated by the pin falling to low. 

The idea then is to figure out which which switch is closed by making each of the btnCol[] pins INPUT* in turn. One of those pins is pulling tbnRow[row] low, so changing the btnCol[] pin to INPUT will allow btnRow[row] to go back to HIGH (it is set to INPUT_PULLUP). So as soon as it goes HIGH, we know we have found the column with the closed switch in it. Does that make sense?

* I am using INPUT rather than HIGH, because if I make some outputs low and some high at the same, there is a chance of a short-circuit if somehow two buttons in different columns are closed at the same time.

kal9001 .

unread,
May 1, 2015, 6:42:03 AM5/1/15
to barns...@googlegroups.com

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.

--
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.

Paul Beard

unread,
May 1, 2015, 5:11:09 PM5/1/15
to barns...@googlegroups.com
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.

kal9001 .

unread,
May 1, 2015, 9:12:56 PM5/1/15
to barns...@googlegroups.com

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.

Paul Beard

unread,
May 2, 2015, 4:16:07 AM5/2/15
to barns...@googlegroups.com
Kevin, I'm not changing a pin to input to read it and then changing it back to an output. I'm changing a column output pin to input, then reading a completely different pin, a row input pin, then changing the column pin back to output.

The reason I'm changing the column output pin to an input is not to read it, just to stop it pulling the row input pin down (if indeed it is because the button in the same column is closed). I am changing it to an INPUT rather than to HIGH is because with one column pin HIGH and the others LOW, there is danger of a short circuit if switches in more than one column are closed at the same time.

kal9001 .

unread,
May 2, 2015, 5:37:52 AM5/2/15
to barns...@googlegroups.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.

Reply all
Reply to author
Forward
0 new messages