Microcontroller Programming

Alright, this is getting written at 3AM after hours and hours of tears.  I've had problems, trials and tribulations, small triumphs, only to be dashed by cruel, cruel microcontroller behavior.  I will detail my problems and solutions at a later date.  Right now, I'm going to list my code (because it looks impressive) and tell you what I (less impressively) was able to accomplish for today.  Updates will be appended at the bottom as I get my stuff in order. 

Also, refer to the PCB making for more info on my coding setup.  PCB

So I wanted to code checkers, where you actually played the computer.  This is coded, and some of it is debugged.  It's a pretty simple intelligence, but it will make a good move without looking ahead.  I still need to code some behavior, like kinging, and not moving forward down the board like some stupid lemming.  However, I don't have enough memory on the tiny45 to do it, by like 10%, and my atmega88 board still needs some work as of 3AM the day before the assignment is due.  So postponement basically.  What I did get done is to have the board print out, and you get to make a move, and that move is reflected in the board.  So you could play yourself.  This is just an expedient substitute until the rest of it gets working.  You can't i.e. make a jump, hell you can only make one move really because I haven't coded it to receive an array of chars or ints, i.e. the number 10 is an array of length 2.  So the only move it can make where you type it in is from square 5 to 9.  This will be changed, it's just been a lot of coding and I didn't quite get there.  Again updates appended on the bottom.  Right now is Oct 20.  Below I have pictured a random initialization of the gameboard, which is a collection of three 32-bit ints (white, black, kings) mapped to a gameboard.  Refer to the gui() method in the code at the bottom.  There's a lot of bitwise operations and tedious stuff.  Some of this was adapted from an earlier undergraduate assignment I coded having to do with chess.  The c file itself is in the directory, so it can be opened formatted. 

Screenshot

In that screenshot, and in the screenshot below, there's some artifacting which omits parts of the gameboard.  In real life, it looks pretty decent.  term.py doesn't do a very good job of formatting character widths, so things don't line up perfectly in places, but for a textual gui, it works ok.

Screenshot

This is the interactive part.  It moved from a fresh board to having made a move, based on what you typed in.  I haven't coded arrayed input and I'm having some typecasting issues between char and int (uint8_t), so right now that is the only move possible, but that's easily fixed if I had more time.  For reference if it's hard to see, it moved from square 5 to 9.  Once I get arrayed input, you could like play yourself a game of checkers, but hopefully I can just skip that and go directly to playing a game against the computer.  We'll see how things go. 


//
//
// Checkers.c from hello.echo.45.c
//
// 9600 baud serial echo hello-world program
//
// Neil Gershenfeld
// CBA MIT 10/5/08
//
// (c) Massachusetts Institute of Technology 2008
// Permission granted for experimental and personal use;
// license for commercial sale available from MIT.
//
//

#include <avr/io.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <stdbool.h>
#include <stdlib.h>

#define tx_pin PB2 // transmit pin
#define rx_pin PB3 // receive pin
#define led_pin PB4 // LED pin
#define bit_delay_time 104 // bit delay, 1/9600 in usec for 8 MHZ/8
#define char_delay_time 1 // char delay time, in ms

#define bit_delay() _delay_us(bit_delay_time) // RS232 bit delay
#define half_bit_delay() _delay_us(bit_delay_time/2) // RS232 half bit delay
#define char_delay() _delay_ms(char_delay_time) // RS232 char delay
#define output(pin) (DDRB |= byte(pin)) // set pin for output
#define set(pin) (PORTB |= byte(pin)) // set pin in PORTB
#define clear(pin) (PORTB &= ~(byte(pin))) // clear pin in PORTB
#define byte(bit) (1 << bit) // byte with bit set
#define bit_get(byte,bit) (byte & (1 << bit)) // test for bit set
#define pin_test(bit) (PINB & (1 << bit)) //test for pin test

#define bit_test(p,m) ((p) & (m))
#define bit_set(p,m) ((p) |= (m))
#define bit_clear(p,m) ((p) &= ~(m))
#define LONGBIT(x) ((unsigned long)0x00000001 << (x))
#define BIT(x) (0x01 << (x))

typedef long bitboard;

typedef uint8_t uint8;

typedef enum {
   empty,
   white,
   black,
   white_king,
   black_king
} piece;

typedef struct {
   bitboard white_pieces;
   bitboard black_pieces;
   bitboard kings;
   bool white_turn;
} gamestate;

typedef struct {
   uint8 src;
   uint8 dest;
   uint8 captured[2];
} move;

uint8 i;
uint8 j;
uint8 k;

uint8 cap_col;
uint8 cap_row;
uint8 cap_pos;
uint8 start_row;
uint8 start_col;
uint8 start_pos;
uint8 end_row;
uint8 end_col;
uint8 end_pos;
   
move mv;
uint8 capture[2];
uint8 num_captures = 0;

gamestate current_state;


uint8 get_int() {
   //
   // read and return a character
   // assumes no line driver (doesn't invert bits)
   //
   uint8 rxbyte=0;
   while (! pin_test(rx_pin))
      //
      // wait for start bit
      //
      ;
   //
   // delay to middle of first data bit
   //
   half_bit_delay();
   bit_delay();
   //
   // read data bits
   //
   if pin_test(rx_pin)
      rxbyte |= (0 << 0);
   else
      rxbyte |= (1 << 0);
   bit_delay();
   if pin_test(rx_pin)
      rxbyte |= (0 << 1);
   else
      rxbyte |= (1 << 1);
   bit_delay();
   if pin_test(rx_pin)
      rxbyte |= (0 << 2);
   else
      rxbyte |= (1 << 2);
   bit_delay();
   if pin_test(rx_pin)
      rxbyte |= (0 << 3);
   else
      rxbyte |= (1 << 3);
   bit_delay();
   if pin_test(rx_pin)
      rxbyte |= (0 << 4);
   else
      rxbyte |= (1 << 4);
   bit_delay();
   if pin_test(rx_pin)
      rxbyte |= (0 << 5);
   else
      rxbyte |= (1 << 5);
   bit_delay();
   if pin_test(rx_pin)
      rxbyte |= (0 << 6);
   else
      rxbyte |= (1 << 6);
   bit_delay();
   if pin_test(rx_pin)
      rxbyte |= (0 << 7);
   else
      rxbyte |= (1 << 7);
   return rxbyte;
}
  
char get_char() {
   //
   // read and return a character
   // assumes no line driver (doesn't invert bits)
   //
   unsigned char rxbyte=0;
   while (! pin_test(rx_pin))
      //
      // wait for start bit
      //
      ;
   //
   // delay to middle of first data bit
   //
   half_bit_delay();
   bit_delay();
   //
   // read data bits
   //
   if pin_test(rx_pin)
      rxbyte |= (0 << 0);
   else
      rxbyte |= (1 << 0);
   bit_delay();
   if pin_test(rx_pin)
      rxbyte |= (0 << 1);
   else
      rxbyte |= (1 << 1);
   bit_delay();
   if pin_test(rx_pin)
      rxbyte |= (0 << 2);
   else
      rxbyte |= (1 << 2);
   bit_delay();
   if pin_test(rx_pin)
      rxbyte |= (0 << 3);
   else
      rxbyte |= (1 << 3);
   bit_delay();
   if pin_test(rx_pin)
      rxbyte |= (0 << 4);
   else
      rxbyte |= (1 << 4);
   bit_delay();
   if pin_test(rx_pin)
      rxbyte |= (0 << 5);
   else
      rxbyte |= (1 << 5);
   bit_delay();
   if pin_test(rx_pin)
      rxbyte |= (0 << 6);
   else
      rxbyte |= (1 << 6);
   bit_delay();
   if pin_test(rx_pin)
      rxbyte |= (0 << 7);
   else
      rxbyte |= (1 << 7);
   return rxbyte;
}



void put_char(char txchar) {
   //
   // print the character in txchar
   // assumes no line driver (doesn't invert bits)
   //
   // start bit
   //
   set(tx_pin);
   bit_delay();
   //
   // data bits
   //
   if bit_get(txchar,0)
      clear(tx_pin);
   else
      set(tx_pin);
   bit_delay();
   if bit_get(txchar,1)
      clear(tx_pin);
   else
      set(tx_pin);
   bit_delay();
   if bit_get(txchar,2)
      clear(tx_pin);
   else
      set(tx_pin);
   bit_delay();
   if bit_get(txchar,3)
      clear(tx_pin);
   else
      set(tx_pin);
   bit_delay();
   if bit_get(txchar,4)
      clear(tx_pin);
   else
      set(tx_pin);
   bit_delay();
   if bit_get(txchar,5)
      clear(tx_pin);
   else
      set(tx_pin);
   bit_delay();
   if bit_get(txchar,6)
      clear(tx_pin);
   else
      set(tx_pin);
   bit_delay();
   if bit_get(txchar,7)
      clear(tx_pin);
   else
      set(tx_pin);
   bit_delay();
   //
   // stop bit
   //
   clear(tx_pin);
   bit_delay();
   }

void print_string(char *str) {
   //
   // print the null-terminated program memory string str
   //
   while (pgm_read_byte(str) != 0x00)
      put_char(pgm_read_byte(str++));
      char_delay();
   }

void blink() {
   //
   // blink the LED
   //
   set(led_pin);
   _delay_ms(10);
   clear(led_pin);
   }



piece get_piece(uint8 position)
{
   if (bit_test(current_state.kings, LONGBIT(position - 1))) {
      if (bit_test(current_state.white_pieces, LONGBIT(position - 1)))
         return white_king;
      else
         return black_king;
   }
   else {
      if (bit_test(current_state.white_pieces, LONGBIT(position - 1)))
         return white;
      else if (bit_test(current_state.black_pieces, LONGBIT(position - 1)))
         return black;
      else return empty;
   }
}

void set_piece(uint8 position, piece type) {
   if (type == empty) {
      bit_clear(current_state.white_pieces, LONGBIT(position - 1));
      bit_clear(current_state.black_pieces, LONGBIT(position - 1));
      bit_clear(current_state.kings, LONGBIT(position - 1));
      return;
   }
  
   if (type == white_king || type == black_king)
      bit_set(current_state.kings, LONGBIT(position - 1));
  
   if (type == white || type == white_king)
      bit_set(current_state.white_pieces, LONGBIT(position - 1));
   else
      bit_set(current_state.black_pieces, LONGBIT(position - 1));
}

void move_piece(uint8 position_from, uint8 position_to) {
   set_piece(position_to, get_piece(position_from));
   set_piece(position_from, empty);
}
/*
void (uint8 position) {
   piece current;

   current = get_piece(position);
   if (current == white)
      set_piece(position, white_king);
   else if (current == black)
      set_piece(position, black_king);
}
*/
void capture_piece(uint8 position) {
   set_piece(position, empty);
}

//char guimessage[] PROGMEM = "Hope that you can read this:\n";
void guitest() {
    while (1) {
        print_string("But can you read this?\n");
        _delay_ms(1000);
    }
}

char gui1[] PROGMEM =   "\n           1";
char gui2[] PROGMEM =  "\n|-------|-------|-------|-------|-------|-------|-------|-------|\n|";
char gui3[] PROGMEM =     "       | WK |";
char gui4[] PROGMEM =     "       |  W  |";
char gui5[] PROGMEM =     "       | BK |";
char gui6[] PROGMEM =     "       |   B  |";
char gui7[] PROGMEM =     "       |   E  |";
char gui8[] PROGMEM =        " WK |       |";
char gui9[] PROGMEM =       "  W  |       |";
char gui10[] PROGMEM =       " BK |       |";
char gui11[] PROGMEM =     "  B   |       |";
char gui12[] PROGMEM =     "  E   |       |";
char gui13[] PROGMEM = "  29\n\n";

void gui() {
  
   uint8 ccounter = 0; //these counters are a little bit ugly
   uint8 rcounter = 0;
   uint8 rswitch = 0;
   //print_string("\r       1\r");
   print_string(gui1);
   print_string(gui2);
   //print_string("\r|----|----|----|----|----|----|----|----|\r|");
   for (i=0; i<32; i++) {
      if (ccounter == 4) {
         //print_string("\r|----|----|----|----|----|----|----|----|\r|");
         print_string(gui2);
         ccounter = 0;
         rcounter = 1;
      }
      else if (rswitch == 4) {
         //print_string("\r|----|----|----|----|----|----|----|----|\r|");
         print_string(gui2);
         rswitch = 0;
         rcounter = 0;
      }
      if (rcounter == 0)
      {
         if (bit_test(current_state.white_pieces,LONGBIT(i))) {
            if (bit_test(current_state.kings,LONGBIT(i)))
               //print_string("    | WK |");
               print_string(gui3);
            else //print_string("    | W  |");
                print_string(gui4);
         }
         else if (bit_test(current_state.black_pieces,LONGBIT(i))) {
            if (bit_test(current_state.kings, LONGBIT(i)))
               //print_string("    | BK |");
               print_string(gui5);
            else //print_string("    | B  |");
                print_string(gui6);
         }
         else //print_string("    | E  |");
            print_string(gui7);
         ccounter++;
      }  
      else if (rcounter == 1) {
         if (bit_test(current_state.white_pieces,LONGBIT(i))) {
            if (bit_test(current_state.kings,LONGBIT(i)))
               //print_string(" WK |    |");
               print_string(gui8);
            else //print_string(" W  |    |");
                print_string(gui9);
         }
         else if (bit_test(current_state.black_pieces, LONGBIT(i))) {
            if (bit_test(current_state.kings, LONGBIT(i)))
               //print_string(" BK |    |");
                print_string(gui10);
            else //print_string(" B  |    |");
                print_string(gui11);
         }
         else //print_string(" E  |    |");
            print_string(gui12);
         rswitch++;
      }
   }
   print_string(gui2);
   print_string(gui13);
}
/*
signed short evaluate() {
 
  signed short quad_white = 0;
  signed short quad_black = 0;
  uint8 wcount = 0;
  uint8 bcount = 0;
 
  for (i=1; i<=8; i++) {
    for (j=0; j<4; j++) {
      if (!bit_test(current_state.kings,LONGBIT((i-1)*4+j))) {
        if (bit_test(current_state.white_pieces,LONGBIT((i-1)*4+j)))
          wcount++;
        else if (bit_test(current_state.black_pieces,LONGBIT((i-1)*4+j)))
          bcount++;
      }
      else {
        if (bit_test(current_state.white_pieces,LONGBIT((i-1)*4+j))) {
          quad_white += 10;
          if (j == 1 || j == 4)
            quad_white -= 2;
        }
        else {
          quad_black += 10;
          if (j == 1 || j == 4)
            quad_black -= 2;
        }
      }
    }
    quad_white += (8-i)*(8-i)*wcount;
    quad_black += i*i*bcount;
   
    wcount = 0;
    bcount = 0;
  }
 
    
  return quad_white - quad_black;// + (rand_num-0.5)*10;
}*/

uint8 isvalidmove(uint8 start_pos, uint8 end_pos, uint8 start_row, uint8 start_col, uint8 end_row, uint8 end_col) {
    //method returns 3 different states, 0 for illegal move, 1 for legal move, and end_pos for incomplete move (meaning another jump may be possible)
  
    if (bit_test(current_state.white_pieces,LONGBIT(end_pos)) || bit_test(current_state.black_pieces,LONGBIT(end_pos))) //checks if end_pos is empty
       return 0;
   
    //not sure if implementing the following if statement will end up being necessary
    if (!bit_test(current_state.white_pieces,LONGBIT(start_pos)) && !bit_test(current_state.black_pieces,LONGBIT(start_pos)))
       return 0;
  
    if (bit_test(current_state.white_pieces,LONGBIT(start_pos)) && !current_state.white_turn)
       return 0;
  
    if (bit_test(current_state.black_pieces,LONGBIT(start_pos)) && current_state.white_turn)
       return 0;
  
    if (start_col - end_col == 1 || end_col - start_col == 1) { //one square diagonally (abs method didn't want to work here)
    
        if (bit_test(current_state.white_pieces, LONGBIT(start_pos)) && current_state.white_turn) { //test white pieces first
        
            if (end_row - start_row == -1)  //note the numerical layout of the board, which causes sign
                return 1;   //if doesn't cause capture, and only jumps one space diagonal forward, return true
            if (end_row - start_row == 1 && bit_test(current_state.kings, LONGBIT(start_pos)))
                return 1;
         }
       
        else if (bit_test(current_state.black_pieces, LONGBIT(start_pos)) && !current_state.white_turn) {  //now test black pieces
       
            if (end_row - start_row == 1)
                return 1;
            if (end_row - start_row == -1 && bit_test(current_state.kings, LONGBIT(start_pos)))
                return 1;
        }
    }
   
    else if(start_col - end_col == 2 || start_col - end_col == -2) {  //now see about capture moves  Note: this method only does one capture, then calls another method to recur
      //again, abs wasn't working for above
     
        cap_col = (start_col + end_col)/2;
        cap_row = (start_row + end_row)/2;
           
        //converting back to 1-32 board representation
        if (cap_row == 1 || cap_row == 3 || cap_row == 5 || cap_row == 7)
            cap_pos = (cap_row - 1)*4 + cap_col/2;
        else
            cap_pos = (cap_row - 1)*4 + 1 + (cap_col-1)/2;
     

        if (start_row - end_row != 2 && start_row - end_row != -2)
            return 0;
     
        else if(bit_test(current_state.white_pieces, LONGBIT(start_pos)) && current_state.white_turn) {
            if(!bit_test(current_state.black_pieces, LONGBIT(cap_pos-1)))  //eliminate no black pieces in captured square (also a jump of 3 rows)
                return 0;
      
            if(!bit_test(current_state.kings, LONGBIT(start_pos))) { //eliminate pawns jumping backwards, or 5 rows
                if(end_row - start_row != -2)
                    return 0;
            }   
        }
    }
   
    else if(bit_test(current_state.black_pieces, LONGBIT(start_pos)) && !current_state.white_turn) {
        if(!bit_test(current_state.white_pieces, LONGBIT(cap_pos-1)))
            return 0;
        if(!bit_test(current_state.kings, LONGBIT(start_pos)))  {
            if(end_row - start_row != 2)
               return 0;
        }
    }
   
    return end_pos; //used to be return 2 i.e. another jump may be possible
   
}


uint8 row(uint8 index) {
    uint8 start_row = 1;
    if (bit_test(j, BIT(2)))
        start_row += 1;
    if (bit_test(j, BIT(3)))
        start_row += 2;
    if (bit_test(j, BIT(4)))
        start_row += 4;
    return start_row;
}

uint8 col(uint8 index) {
    uint8 start_col = 1;
    //these should be listed for 8x8 #s
    
    if(!bit_test(j, BIT(2)))  //set which includes first row in decimal format
        start_col += 1;
    if(bit_test(j, BIT(1)))
        start_col += 4;
    if(bit_test(j, BIT(0)))
        start_col += 2;
      
    return start_col;
}


/*
void best_move() {
      
    //uint8 cap;
    //uint8 temp;
    uint8 test;
    uint8 test2;
    signed short eval;
    signed short best = 0;
     
    for (j=0; j<32; j++) { //all start positions
        start_pos = j;
        start_col = col(j);
        start_row = row(j);
       
        for (k=0; k<32; k++) {   //all end positions
            num_captures = 0;
           
            end_pos = k;
            end_col = col(k);
            end_row = row(k);
         
            test = isvalidmove(start_pos,end_pos,start_row,start_col,end_row,end_col);
            if (test == 0)
                continue;
                     
            if (test != 1) { //one jump possible, now look for a second
                cap_col = (start_col + end_col)/2;
                cap_row = (start_row + end_row)/2;
                  
                //converting back to 1-32 board representation NOTE: NOT 0-31, in contrast to start_pos
                if (cap_row == 1 || cap_row == 3 || cap_row == 5 || cap_row == 7)
                    cap_pos = (cap_row - 1)*4 + cap_col/2;
                else
                    cap_pos = (cap_row - 1)*4 + 1 + (cap_col-1)/2;
               
                capture[num_captures++] = cap_pos;
   
                //now change values to those for next possible jump
                start_row = end_row;
                start_col = end_col;
                end_row = row(test);
                end_col = col(test);
               
                test2 = isvalidmove(end_pos, test, start_row, start_col, end_row, end_col);
                if (test2 != 1 && test2 != 0) { //second jump, stop here due to memory limitation (and no recursion)
                    cap_col = (start_col + end_col)/2;
                    cap_row = (start_row + end_row)/2;
                  
                    //converting back to 1-32 board representation NOTE: NOT 0-31, in contrast to start_pos
                    if (cap_row == 1 || cap_row == 3 || cap_row == 5 || cap_row == 7)
                        cap_pos = (cap_row - 1)*4 + cap_col/2;
                    else
                        cap_pos = (cap_row - 1)*4 + 1 + (cap_col-1)/2;
                   
                    capture[num_captures++] = cap_pos;
                    end_pos = test2;     
                }
            }
          
            //test = 1 just shorts to here
            move_piece(start_pos, end_pos); //start_col, start_row, end_col, end_row get changed around, but not the pos variables
            eval = evaluate();
            move_piece(end_pos, start_pos); //undo the speculative move
           
            if (current_state.white_turn) {       
                if (eval > best) {
                    best = eval;
                    mv.src = start_pos;
                    mv.dest = end_pos;
                    for (i=0; i<num_captures; i++)
                        mv.captured[i] = capture[i];
                }
            }
           
            else  { //for black this number is negative
                if (eval < best) {
                    best = eval;
                    mv.src = start_pos;
                    mv.dest = end_pos;
                    for (i=0; i<num_captures; i++)
                        mv.captured[i] = capture[i];
                }
            }
             
        }
      
    }
    move_piece(mv.src, mv.dest);
    for (i=0; i<num_captures; i++)
        capture_piece(mv.captured[i]);
}
*/
char message1[] PROGMEM = "Your Move Starting Square# (1-32):";
char message2[] PROGMEM = "Your Move Ending Square# (1-32):";
char init[] PROGMEM = "Initializing game, refer to gui, you're black, get the first move\n";
char update[] PROGMEM = "45's move:";

char message3[] PROGMEM = "Separation\n";
char message3[] PROGMEM = "Move is indeed a valid move\n";


int main(void) {
   
    uint8 rxbyte1;
    uint8 rxbyte2;
    uint8 counter = 0;
    uint8 test = 0;
   
    bitboard white_pieces =    0b11111111000000000000000000000000;  //last bit is the first square in decimal
    bitboard black_pieces =    0b00000000000000000000000011111111;
    bitboard kings =           0b00000000000000000000000000000000;
   
    current_state.white_pieces = white_pieces;
    current_state.black_pieces = black_pieces;
    current_state.kings = kings;
   
    output(tx_pin);
    output(led_pin);
    clear(tx_pin);
   
   
    while(counter < 6 ) { //deals with the gibberish that gets printed.
        print_string(message3);
        _delay_ms(1000);
        counter++;
        }
   
    //guitest();
   
    gui();
   
    //print_string(init);
    //print_string("Initializing game, refer to gui, you're white, get the first move");
    while (1) {
        print_string(message1);
        //rxbyte1 = get_int();
        rxbyte1 = get_char();
        //put_char((char)rxbyte1);
        put_char(rxbyte1);
        put_char('\n');
        blink();
        print_string(message2);
        //rxbyte2 = get_int();
        rxbyte2 = get_char();
        //put_char((char)rxbyte2);
        put_char(rxbyte2);
        put_char('\n');
        blink();
        move_piece((uint8)rxbyte1, (uint8)rxbyte2); //casting here isn't doing anything yet
        move_piece(5,9);
        if isvalidmove(rxbyte1, rxbyte2, row(rxbyte1), col(rxbyte1), row(rxbyte2), col(rxbyte2))
            print_string(message4);
        //move_piece(rxbyte1, rxbyte2);
        //print_string("45's moved");
        put_char('\n');
        //best_move();
        gui();
    }
}
/*char message[] PROGMEM = "rcvd char:";

int main(void) {
   unsigned char rxbyte;
 
   output(tx_pin);
   output(led_pin);
   clear(tx_pin);
  
   while (1) {
      rxbyte = get_char();
      print_string(message);
      put_char(rxbyte);
      put_char('\n');
      blink();
      }
   }*/