#define NUMPIXELS 128 #define NUMROWS 8 #define NUMCOLUMNS 8 #define RGB_BLACK 0 #define RGB_BLUE 1 #define RGB_GREEN 2 #define RGB_CYAN 3 #define RGB_RED 4 #define RGB_MAGENTA 5 #define RGB_BROWN 6 #define RGB_LIGHTGRAY 7 #define RGB_DARKGRAY 8 #define RGB_YELLOW 14 #define RGB_WHITE 15 #include "./NPLib2x64.c" #include #include #include #include #include // NPLib API // void initialise(); // void show(void); // void setPixel(int pixelNumber, unsigned char R, unsigned char G, unsigned char B); // void setPixelRC(int row, int column, unsigned char R, unsigned char G, unsigned char B); // void clearPixel(int pixelNumber); // void clearbuffer(); // prototypes void pause(unsigned int p); void palette(int n, uint8_t *R, uint8_t *G, uint8_t *B); uint8_t able_to_move_in_direction(uint8_t proposed_dir); void move_snek(); uint8_t turn_snek(); uint8_t turn_snek_cw(); uint8_t turn_snek_acw(); void new_apple(); bool randomly_change_dir(); void clear_grid(); void printgrid(); // debug // globals #define SNEK_ROW 0 #define SNEK_COL 1 // grid is our record of what's where, a 2D copy of the display uint8_t grid[8][16]={0}; struct point { uint8_t row; uint8_t col; }; struct point snek[NUMPIXELS]; // snek is a circular buffer containing the co-ordinatess of the body segments // refer to coordinates like this: snek[pos_tail].row and snek[pos_tail].col // to move the snake, a new point is added at the head, and one removed from the tail // position in the circ buffer of the snake's head and tail. uint8_t pos_tail, pos_head; #define DIR_UP 0 #define DIR_RIGHT 1 #define DIR_DOWN 2 #define DIR_LEFT 3 #define DIR_NONE 4 uint8_t snek_dir; #define UNOCCUPIED 0 #define OCCUPIED_BODY 1 #define OCCUPIED_APPLE 2 #define EDGE_OF_GRID 3 int main() { uint8_t R,G,B; // always initialise NUMPIXELS initialise(NUMPIXELS); printf("8x16 Snek v0.1\nS Dixon 2022\n\npress the 'any' key to continue.\n\n"); // use this delay to randomise unsigned t; while (true) { if(kbhit()){ char ch = getch(); // eat it break; } t++; } srand(t); bool quit = false; bool lost = false; while (quit==false) { lost = false; // set up clearbuffer(NUMPIXELS); clear_grid(); // initial position of snake grid[4][3] = OCCUPIED_BODY; snek[0].row = 4; snek[0].col = 3; pos_tail = 0; palette(RGB_BLUE,&R,&G,&B); setPixelRC(4, 3, R, G, B); grid[4][4] = OCCUPIED_BODY; snek[1].row = 4; snek[1].col = 4; pos_head = 1; setPixelRC(4, 4, R, G, B); snek_dir = DIR_RIGHT; new_apple(); // looks for unoccupied spot show(); // run loop while (lost == false && quit == false) { if(able_to_move_in_direction(snek_dir) == 0) { if(randomly_change_dir()) { uint8_t occ = able_to_move_in_direction(snek_dir); if(occ == OCCUPIED_APPLE) { move_snek(true); //printf("new_apple point a\n\r"); new_apple(); } else { move_snek(false); } } else { move_snek(false); } } else if(able_to_move_in_direction(snek_dir) == OCCUPIED_APPLE) { move_snek(true); //printf("new_apple point b\n\r"); new_apple(); } else { uint8_t new_dir = turn_snek(); if(new_dir == DIR_NONE) { // printf("stopped by %d\r\n",able_to_move_in_direction(snek_dir)); lost = true; } else { snek_dir = new_dir; uint8_t occ = able_to_move_in_direction(snek_dir); if(occ == OCCUPIED_APPLE) { move_snek(true); //printf("new_apple point a\n\r"); new_apple(); } else { move_snek(false); } } } //printf("run loop point c, grid is now\n\r"); //printgrid(); show(); if(kbhit()) { // press a key to quit quit = true; char ch = getch(); // eat it //printgrid(); pause(65535); } pause(2048); } // end run loop } // end quit loop return 0; } void new_apple() { uint8_t r,c; uint8_t R,G,B; do{ r = rand() % 8; c = rand() % 16; } while (grid[r][c]!=UNOCCUPIED); grid[r][c] = OCCUPIED_APPLE; //printf("new apple placed at r:%d c:%d\n\r",r,c); //printgrid(); palette(RGB_RED,&R,&G,&B); setPixelRC(r, c, R, G, B); } uint8_t turn_snek() { if(rand() % 100 > 50){ // 50% of the time return turn_snek_acw(); } else{ return turn_snek_cw(); } } bool randomly_change_dir() { // once in a while, pick a different direction. If it's free, take it. if(rand() % 100 > 25) // X% of the time { return false; } int random_dir = rand() % 4; // 0,1,2,or 3 which are the 4 directions if(random_dir != snek_dir) { uint8_t occ = able_to_move_in_direction(random_dir); if(occ == 0 || occ == OCCUPIED_APPLE) { snek_dir = random_dir; return true; } } // else return false; } // returns the new direction uint8_t turn_snek_acw() { uint8_t occ; // try UP occ = able_to_move_in_direction(DIR_UP); if(occ==0 || occ==OCCUPIED_APPLE){ return DIR_UP; } // try LEFT occ = able_to_move_in_direction(DIR_LEFT); if(occ==0 || occ==OCCUPIED_APPLE){ return DIR_LEFT; } // try DOWN occ = able_to_move_in_direction(DIR_DOWN); if(occ==0 || occ==OCCUPIED_APPLE){ return DIR_DOWN; } // try RIGHT occ = able_to_move_in_direction(DIR_RIGHT); if(occ==0 || occ==OCCUPIED_APPLE){ return DIR_RIGHT; } // default return DIR_NONE; } // returns the new direction uint8_t turn_snek_cw() { uint8_t occ; // try UP occ = able_to_move_in_direction(DIR_UP); if(occ==0 || occ==OCCUPIED_APPLE){ return DIR_UP; } // try RIGHT occ = able_to_move_in_direction(DIR_RIGHT); if(occ==0 || occ==OCCUPIED_APPLE){ return DIR_RIGHT; } // try DOWN occ = able_to_move_in_direction(DIR_DOWN); if(occ==0 || occ==OCCUPIED_APPLE){ return DIR_DOWN; } // try LEFT occ = able_to_move_in_direction(DIR_LEFT); if(occ==0 || occ==OCCUPIED_APPLE){ return DIR_LEFT; } // default return DIR_NONE; } void move_snek(bool eat){ uint8_t R,G,B; // calculate new head r,c uint8_t new_head_r = snek[pos_head].row; uint8_t new_head_c = snek[pos_head].col; switch(snek_dir){ case DIR_UP: new_head_r--; break; case DIR_RIGHT: new_head_c++; break; case DIR_DOWN: new_head_r++; break; case DIR_LEFT: new_head_c--; break; } if(eat==false) { // clear tail palette(RGB_BLACK,&R,&G,&B); setPixelRC(snek[pos_tail].row, snek[pos_tail].col, R, G, B); grid[snek[pos_tail].row][snek[pos_tail].col] = UNOCCUPIED; // advance tail pointer pos_tail++; pos_tail = pos_tail % NUMPIXELS; // wrap at NUMPIXELS } // print new head palette(RGB_BLUE,&R,&G,&B); setPixelRC(new_head_r, new_head_c, R, G, B); grid[new_head_r][new_head_c] = OCCUPIED_BODY; // advance head pointers pos_head++; pos_head = pos_head % NUMPIXELS; // wrap at NUMPIXELS snek[pos_head].row = new_head_r; snek[pos_head].col = new_head_c; } // returns 0 if free to move uint8_t able_to_move_in_direction(uint8_t proposed_dir) { uint8_t headrow = snek[pos_head].row; uint8_t headcol = snek[pos_head].col; // first check grid switch(proposed_dir) { case DIR_UP: if(headrow==0) return EDGE_OF_GRID; else headrow--; break; case DIR_RIGHT: if(headcol==15) return EDGE_OF_GRID; else headcol++; break; case DIR_DOWN: if(headrow==7) return EDGE_OF_GRID; else headrow++; break; case DIR_LEFT: if(headcol==0) return EDGE_OF_GRID; else headcol--; break; } // now check actual square - headrow,headcol uint8_t occupied = grid[headrow][headcol]; if(occupied==OCCUPIED_BODY || occupied==OCCUPIED_APPLE) return occupied; return 0; // OK } void clear_grid() { for(int r = 0; r < 8; r++) { for(int c = 0; c < 16; c++) { grid[r][c] = UNOCCUPIED; } } } void pause(unsigned int p) { // busy loop for(int i = 0;i < p; i++) { // } } void palette(int n, uint8_t *R, uint8_t *G, uint8_t *B ) { switch(n) { case RGB_BLUE: *R = 0; *G = 0; *B = 32; break; case RGB_GREEN: *R = 0; *G = 32; *B = 0; break; case RGB_CYAN: *R = 0; *G = 32; *B = 32; break; case RGB_RED: *R = 32; *G = 0; *B = 0; break; case RGB_MAGENTA: *R = 32; *G = 0; *B = 32; break; case RGB_BROWN: *R = 32; *G = 16; *B = 0; break; case RGB_LIGHTGRAY: *R = 16; *G = 16; *B = 16; break; case RGB_DARKGRAY: *R = 8; *G = 8; *B = 8; break; case RGB_YELLOW: *R = 32; *G = 32; *B = 0; break; case RGB_WHITE: *R = 32; *G = 32; *B = 32; break; default: *R = 0; *G = 0; *B = 0; break; } } // debug tool void printgrid() { for(int r = 0; r < 8; r++) { for(int c = 0; c < 16; c++) { printf("%d", grid[r][c]); } printf("\r\n"); } uint8_t pos = pos_tail; while(pos!=pos_head){ printf("%d,%d ",snek[pos].row,snek[pos].col); pos++; pos = pos % 64; // wrap at 64 } printf("\n\r"); }