Created
December 21, 2020 15:37
-
-
Save mb2532/f1647371b308368f94976e532f8f042e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/////////////////////////////////////////////////////////////////////// | |
/// 640x480 Connect 4 Board Display | |
/// ECE5760 Final Project | |
/// Cornell University Fall 2020 | |
/// Rohan Agarwal (ra462), Megan Backus (mb2532), Andrew Tsai (aht53) | |
/// Professor Hunter Adams, Professor Bruce Land | |
/// DE1 computer | |
/// compile with: | |
/// gcc connectFourVGA.c -o connect_four -lm -lpthread | |
/////////////////////////////////////////////////////////////////////// | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <unistd.h> | |
#include <fcntl.h> | |
#include <sys/types.h> | |
#include <sys/ipc.h> | |
#include <sys/shm.h> | |
#include <sys/mman.h> | |
#include <sys/time.h> | |
#include <math.h> | |
#include <string.h> | |
#include <pthread.h> | |
#include "address_map_arm_brl4.h" | |
/* | |
// video display | |
#define SDRAM_BASE 0xC0000000 | |
#define SDRAM_END 0xC3FFFFFF | |
#define SDRAM_SPAN 0x04000000 | |
// characters | |
#define FPGA_CHAR_BASE 0xC9000000 | |
#define FPGA_CHAR_END 0xC9001FFF | |
#define FPGA_CHAR_SPAN 0x00002000 | |
*/ | |
/* Cyclone V FPGA devices */ | |
/* | |
#define HW_REGS_BASE 0xff200000 | |
//#define HW_REGS_SPAN 0x00200000 | |
#define HW_REGS_SPAN 0x00005000 | |
*/ | |
// new PIO ports | |
void *h2p_virtual_base; | |
#define FPGA_AXI_BASE 0xC0000000 | |
#define FPGA_AXI_SPAN 0x04001000 | |
/* | |
// On-Chip SRAM | |
#define FPGA_ONCHIP_BASE 0xC8000000 | |
#define FPGA_ONCHIP_SPAN 0x00080000 | |
*/ | |
// Board Dimensions | |
#define NUM_ROWS 6 | |
#define NUM_COLS 7 | |
// Helper Functions | |
#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y)) | |
// configurable global variable to control hps-->fpga clock period | |
#define CLOCK_DELAY 1000000 | |
#define FPGA_RESET_BASE 0x04000010 | |
// initialize PIO port pointers | |
volatile int * reset_ptr = NULL ; | |
// graphics primitives | |
void VGA_text(int, int, char *); | |
void VGA_text_clear(); | |
void VGA_box(int, int, int, int, short); | |
void VGA_rect(int, int, int, int, short); | |
void VGA_line(int, int, int, int, short); | |
void VGA_Vline(int, int, int, short); | |
void VGA_Hline(int, int, int, short); | |
void VGA_disc(int, int, int, short); | |
void VGA_circle(int, int, int, int); | |
// 16-bit primary colors (currently padding 8 bit RGB) | |
#define red 0x00e0//(0 + (0 << 5) + (31 << 11)) | |
#define dark_red (0 + (0 << 5) + (15 << 11)) | |
#define green (0 + (63 << 5) + (0 << 11)) | |
#define dark_green (0 + (31 << 5) + (0 << 11)) | |
#define blue 0x0003 //(31 + (0 << 5) + (0 << 11)) | |
#define dark_blue 0x0099//(15 + (0 << 5) + (0 << 11)) | |
#define yellow 0x00f8 //(0 + (63 << 5) + (31 << 11)) | |
#define cyan (31 + (63 << 5) + (0 << 11)) | |
#define magenta (31 + (0 << 5) + (31 << 11)) | |
#define black (0x0000) | |
#define gray (15 + (31 << 5) + (51 << 11)) | |
#define white (0x00ff) | |
int colors[] = {red, dark_red, green, dark_green, blue, dark_blue, | |
yellow, cyan, magenta, gray, black, white}; | |
// pixel macro | |
#define VGA_PIXEL(x,y,color) do{\ | |
char *pixel_ptr ;\ | |
pixel_ptr = ((char *)vga_pixel_ptr + ((y)*640 + (x)));\ | |
*(char *)pixel_ptr = (color);\ | |
} while(0) | |
// the light weight bus base | |
void *h2p_lw_virtual_base; | |
// pixel buffer | |
volatile unsigned int *vga_pixel_ptr = NULL; | |
void *vga_pixel_virtual_base; | |
// character buffer | |
volatile unsigned int *vga_char_ptr = NULL; | |
void *vga_char_virtual_base; | |
// SRAM HPS-FPGA Interface Buffer | |
volatile unsigned int * sram_ptr = NULL ; | |
void *sram_virtual_base; | |
// /dev/mem file id | |
int fd; | |
// measure time | |
struct timeval t1, t2; | |
double elapsedTime; | |
// helper function prototypes | |
char *decimalToBinary(int, int); | |
int binaryLongToInt(long); | |
int toSevenTwentyHex(char *); | |
void placeDisc(int, int, int); | |
void pullReset(); | |
void updateGameState(int *, int, int, int); | |
int checkGameOver(int *, int *, int); | |
// initialization of reset loop variable | |
int resetStall = 0; | |
// initialize shared mutex variables and flags for resetting + pausing VGA from different threads | |
pthread_mutex_t reset_lock = PTHREAD_MUTEX_INITIALIZER; | |
pthread_mutex_t pause_lock = PTHREAD_MUTEX_INITIALIZER; | |
int resetVGA = 0; | |
int pauseVGA = 0; | |
// function to convert decimal numbers into 7.20 format | |
int toSevenTwentyHex(char *value) { | |
char str[20]; | |
char s[2] = "."; | |
char *token; | |
strcpy(str, value); | |
token = strtok(str, s); | |
//printf("%s\n", token); | |
char *result1; | |
result1 = decimalToBinary(atoi(token), 7); | |
//printf("result1: %s\n\r", result1); | |
token = strtok(NULL, s); | |
printf("%s\n", token); | |
int length = strlen(token); | |
float op1 = atoi(token) / (pow(10, length)); | |
op1 = op1 * 1048576; | |
char *result2; | |
result2 = decimalToBinary((int)op1, 20); | |
//printf("result2: %s\n\r", result2); | |
strcat(result1, result2); | |
//printf("concat result: %s\n\r", result1); | |
long ret; | |
char *placeholder; | |
ret = strtol(result1, &placeholder, 2); | |
//printf("number is %ld\n", ret); | |
printf("number is %d\n", (int)ret); | |
return (int)ret; | |
} | |
// function to convert a decimal number to a binary number | |
char *decimalToBinary(int value, int bitLength) { | |
int c, d, count; | |
char *pointer; | |
count = 0; | |
if (value < 0) | |
printf("negative value\n\r"); | |
pointer = (char *)malloc(bitLength + 1); | |
if (pointer == NULL) | |
exit(EXIT_FAILURE); | |
for (c = bitLength - 1; c >= 0; c--) | |
{ | |
d = value >> c; | |
if (d & 1) | |
*(pointer + count) = 1 + '0'; | |
else | |
*(pointer + count) = 0 + '0'; | |
count++; | |
} | |
*(pointer + count) = '\0'; | |
return pointer; | |
} | |
// function to convert a binary number to an integer | |
int binaryLongToInt(long n) { | |
int dec = 0, i = 0, rem; | |
while (n != 0) | |
{ | |
rem = n % 10; | |
n /= 10; | |
dec += rem * pow(2, i); | |
++i; | |
} | |
return dec; | |
} | |
void placeDisc(int x, int y, int player){ | |
int box_width = 640/NUM_COLS; | |
int box_height = 480/NUM_ROWS; | |
if(x>=NUM_COLS || y>=NUM_ROWS || x<0 || y<0){ | |
printf("ERROR: Invalid placement\n"); | |
return; | |
} | |
y = (NUM_ROWS - 1) - y; | |
if(player==1){ // AI | |
VGA_disc(x*box_width+(box_width/2), y*box_height+(box_height/2), MIN(box_width, box_height)*0.40, red); | |
} | |
else if(player==2){ // USER | |
VGA_disc(x*box_width+(box_width/2), y*box_height+(box_height/2), MIN(box_width, box_height)*0.40, yellow); | |
} | |
else printf("ERROR: Invalid player\n"); | |
} | |
// helper function for raising integers to a power | |
int int_pow(int base, int exp) { | |
int result = 1; | |
while (exp) | |
{ | |
if (exp % 2) | |
result *= base; | |
exp /= 2; | |
base *= base; | |
} | |
return result; | |
} | |
// function to update our game state data structure | |
void updateGameState(int * game_state_row, int row_to_place, int column_to_place, int player) { | |
//need a new 14 bit value; effectively add some integer to the previous value in game_state_row[row_to_place] | |
//the integer to add is based on the column placed (move) and which player made the move | |
//column to place == 0 means bits 0+1, need to add either 1 or 2 (AI or player) | |
//column_to_place == 1 means bits 2+3, need to add either 4 or 8 (AI or player) | |
//column_to_place == 2 means bits 4+5, need to add either 16 or 32 (AI or player) | |
//2^(2*column_to_place + player - 1) | |
int prevGameState = game_state_row[row_to_place]; | |
int exponent = 2*column_to_place + player - 1; | |
game_state_row[row_to_place] = prevGameState + int_pow(2, exponent); | |
} | |
// function to checks if the game is over | |
// returns 0 if game not over, 1 if game over and the player who just played won, 2 if game is a tie | |
int checkGameOver(int * game_state_row, int * row_to_place, int player_who_just_played) { | |
int i, j; | |
int game_over = 0; | |
int player_array[NUM_ROWS][NUM_COLS]; | |
// Check if player_who_just_played won by 4 in a row | |
// Do a mod 256, if it matches 10101010 (or 01010101, depending on player), they win | |
// Keep shifting and checking until you shift by 2 (NUM_COLS - 4) | |
int intermediate_row = 0; | |
for(i = 0; i < NUM_ROWS; i++) { | |
for(j = 0; j < (NUM_COLS - 3); j++) { | |
intermediate_row = (game_state_row[i] >> 2*j) % 256; | |
if(player_who_just_played == 1) { | |
// Check 01010101 | |
if(intermediate_row == 85) { | |
game_over = 1; | |
return game_over; | |
} | |
else continue; | |
} | |
else if(player_who_just_played == 2) { | |
// Check 10101010 | |
if(intermediate_row == 170) { | |
game_over = 1; | |
return game_over; | |
} | |
else continue; | |
} | |
else { | |
printf("Player who just played is not 1 or 2! I don't know what to do! TODO: Fix this error case.\n\r"); | |
} | |
} | |
} | |
// Check if player_who_just_played won by 4 in a column | |
// Start from rightmost column, bottommost row, and check if they all match the player's token | |
// Keep going across all columns, and until you are 3 rows from the top of the board | |
// Can theoretically be made more efficient by shifting vertical_tracker values for adjacent checks (i.e. if go up rows first, instead of columns) | |
int vertical_tracker[4]; | |
for(i = 0; i < (NUM_ROWS - 3); i++) { | |
for(j = 0; j < NUM_COLS; j++) { | |
vertical_tracker[0] = (game_state_row[i] >> 2*j) % 4; | |
vertical_tracker[1] = (game_state_row[i+1] >> 2*j) % 4; | |
vertical_tracker[2] = (game_state_row[i+2] >> 2*j) % 4; | |
vertical_tracker[3] = (game_state_row[i+3] >> 2*j) % 4; | |
if(player_who_just_played == 1) { | |
// Check if all 4 locations in the column are 01 | |
if((vertical_tracker[0] == 1) && (vertical_tracker[1] == 1) && (vertical_tracker[2] == 1) && (vertical_tracker[3] == 1)) { | |
game_over = 1; | |
return game_over; | |
} | |
else continue; | |
} | |
else if(player_who_just_played == 2) { | |
// Check if all 4 locations in the column are 10 | |
if((vertical_tracker[0] == 2) && (vertical_tracker[1] == 2) && (vertical_tracker[2] == 2) && (vertical_tracker[3] == 2)) { | |
game_over = 1; | |
return game_over; | |
} | |
else continue; | |
} | |
else { | |
printf("Player who just played is not 1 or 2! I don't know what to do! TODO: Fix this error case.\n\r"); | |
} | |
} | |
} | |
// Check if player_who_just_played won by 4 in a diagonal going from lower left to upper right | |
// Start from 4th from rightmost column, bottommost row, and check if they all in upper right diagonal match the player's token | |
// Keep going across all columns, and until you are 3 rows from the top of the board | |
int llur_diag_tracker[4]; | |
for(i = 0; i < (NUM_ROWS - 3); i++) { | |
for(j = 3; j < NUM_COLS; j++) { | |
llur_diag_tracker[0] = (game_state_row[i] >> 2*j) % 4; | |
llur_diag_tracker[1] = (game_state_row[i+1] >> 2*(j-1)) % 4; | |
llur_diag_tracker[2] = (game_state_row[i+2] >> 2*(j-2)) % 4; | |
llur_diag_tracker[3] = (game_state_row[i+3] >> 2*(j-3)) % 4; | |
if(player_who_just_played == 1) { | |
// Check if all 4 locations in the column are 01 | |
if((llur_diag_tracker[0] == 1) && (llur_diag_tracker[1] == 1) && (llur_diag_tracker[2] == 1) && (llur_diag_tracker[3] == 1)) { | |
game_over = 1; | |
return game_over; | |
} | |
else continue; | |
} | |
else if(player_who_just_played == 2) { | |
// Check if all 4 locations in the column are 10 | |
if((llur_diag_tracker[0] == 2) && (llur_diag_tracker[1] == 2) && (llur_diag_tracker[2] == 2) && (llur_diag_tracker[3] == 2)) { | |
game_over = 1; | |
return game_over; | |
} | |
else continue; | |
} | |
else { | |
printf("Player who just played is not 1 or 2! I don't know what to do! TODO: Fix this error case.\n\r"); | |
} | |
} | |
} | |
// Check if player_who_just_played won by 4 in a diagonal going from lower right to upper left | |
// Start from rightmost column, bottommost row, and check if they all in upper left diagonal match the player's token | |
// Keep going across columns until you are 3 columns from the left, and until you are 3 rows from the top of the board | |
int lrul_diag_tracker[4]; | |
for(i = 0; i < (NUM_ROWS - 3); i++) { | |
for(j = 0; j < (NUM_COLS - 3); j++) { | |
lrul_diag_tracker[0] = (game_state_row[i] >> 2*j) % 4; | |
lrul_diag_tracker[1] = (game_state_row[i+1] >> 2*(j+1)) % 4; | |
lrul_diag_tracker[2] = (game_state_row[i+2] >> 2*(j+2)) % 4; | |
lrul_diag_tracker[3] = (game_state_row[i+3] >> 2*(j+3)) % 4; | |
if(player_who_just_played == 1) { | |
// Check if all 4 locations in the column are 01 | |
if((lrul_diag_tracker[0] == 1) && (lrul_diag_tracker[1] == 1) && (lrul_diag_tracker[2] == 1) && (lrul_diag_tracker[3] == 1)) { | |
game_over = 1; | |
return game_over; | |
} | |
else continue; | |
} | |
else if(player_who_just_played == 2) { | |
// Check if all 4 locations in the column are 10 | |
if((lrul_diag_tracker[0] == 2) && (lrul_diag_tracker[1] == 2) && (lrul_diag_tracker[2] == 2) && (lrul_diag_tracker[3] == 2)) { | |
game_over = 1; | |
return game_over; | |
} | |
else continue; | |
} | |
else { | |
printf("Player who just played is not 1 or 2! I don't know what to do! TODO: Fix this error case.\n\r"); | |
} | |
} | |
} | |
// If no one has won yet, check if the board is not full | |
// If it isn't, return that the game is not over (0) | |
for(i = 0; i < NUM_COLS; i++) { | |
if(row_to_place[i] < NUM_ROWS) { | |
game_over = 0; | |
return game_over; | |
} | |
} | |
// Game ended in a tie | |
game_over = 2; | |
return game_over; | |
} | |
// function that pulses hps-->fpga reset signal via PIO port | |
void pullReset(){ | |
*(reset_ptr)=0; | |
printf("reset = 0\n\r"); | |
for(resetStall=0;resetStall<10000000;resetStall++){} | |
*(reset_ptr)=1; | |
printf("reset = 1\n\r"); | |
for(resetStall=0;resetStall<10000000;resetStall++){} | |
*(reset_ptr)=0; | |
printf("reset = 0\n\r"); | |
} | |
int main(void) | |
{ | |
// === need to mmap: ======================= | |
// FPGA_CHAR_BASE | |
// FPGA_ONCHIP_BASE | |
// HW_REGS_BASE | |
// === get FPGA addresses ================== | |
// Open /dev/mem | |
if ((fd = open("/dev/mem", (O_RDWR | O_SYNC))) == -1) | |
{ | |
printf("ERROR: could not open \"/dev/mem\"...\n"); | |
return (1); | |
} | |
// get virtual addr that maps to physical | |
h2p_lw_virtual_base = mmap(NULL, HW_REGS_SPAN, (PROT_READ | PROT_WRITE), MAP_SHARED, fd, HW_REGS_BASE); | |
if (h2p_lw_virtual_base == MAP_FAILED) | |
{ | |
printf("ERROR: mmap1() failed...\n"); | |
close(fd); | |
return (1); | |
} | |
//============================================ | |
// === get VGA char addr ===================== | |
// get virtual addr that maps to physical | |
vga_char_virtual_base = mmap(NULL, FPGA_CHAR_SPAN, (PROT_READ | PROT_WRITE), MAP_SHARED, fd, FPGA_CHAR_BASE); | |
if (vga_char_virtual_base == MAP_FAILED) | |
{ | |
printf("ERROR: mmap2() failed...\n"); | |
close(fd); | |
return (1); | |
} | |
// Get the address that maps to the FPGA LED control | |
vga_char_ptr = (unsigned int *)(vga_char_virtual_base); | |
// === get VGA pixel addr ==================== | |
// get virtual addr that maps to physical | |
vga_pixel_virtual_base = mmap(NULL, SDRAM_SPAN, (PROT_READ | PROT_WRITE), MAP_SHARED, fd, SDRAM_BASE); | |
if (vga_pixel_virtual_base == MAP_FAILED) | |
{ | |
printf("ERROR: mmap3() failed...\n"); | |
close(fd); | |
return (1); | |
} | |
// Get the address that maps to the FPGA pixel buffer | |
vga_pixel_ptr = (unsigned int *)(vga_pixel_virtual_base); | |
// === get SRAM HPS-FPGA Interface Buffer parameter addr ========= | |
//TODO: Define FPGA_ONCHIP_SPAN, FPGA_ONCHIP_BASE | |
sram_virtual_base = mmap( NULL, FPGA_ONCHIP_SPAN, ( PROT_READ | PROT_WRITE ), MAP_SHARED, fd, FPGA_ONCHIP_BASE); //fp | |
if( sram_virtual_base == MAP_FAILED ) { | |
printf( "ERROR: mmap3() failed...\n" ); | |
close( fd ); | |
return(1); | |
} | |
// Get the address that maps to the SRAM HPS-FPGA Interface Buffer | |
sram_ptr =(unsigned int *)(sram_virtual_base); | |
// =========================================== | |
// get virtual address for | |
// AXI bus addr | |
h2p_virtual_base = mmap(NULL, FPGA_AXI_SPAN, (PROT_READ | PROT_WRITE), MAP_SHARED, fd, FPGA_AXI_BASE); | |
if (h2p_virtual_base == MAP_FAILED) | |
{ | |
printf("ERROR: mmap3() failed...\n"); | |
close(fd); | |
return (1); | |
} | |
// Get the address that maps to the PIO for reset | |
reset_ptr =(signed int *)(h2p_virtual_base + FPGA_RESET_BASE); | |
// =========================================== | |
/* create a message to be displayed on the VGA | |
and LCD displays */ | |
char text_top_row[40] = "DE1-SoC ARM/FPGA\0"; | |
char text_bottom_row[40] = "Cornell ece5760\0"; | |
char text_next[40] = "Graphics primitives\0"; | |
char num_string[20], time_string[20]; | |
char color_index = 0; | |
int color_counter = 0; | |
// position of disk primitive | |
int disc_x = 0; | |
// position of circle primitive | |
int circle_x = 0; | |
// position of box primitive | |
int box_x = 5; | |
// position of vertical line primitive | |
int Vline_x = 350; | |
// position of horizontal line primitive | |
int Hline_y = 250; | |
// clear the screen | |
VGA_box(0, 0, 640, 480, blue); | |
// clear the text | |
VGA_text_clear(); | |
// initialization of clock loop variable | |
int clockStall = 0; | |
// start the console input function as a new thread | |
/* | |
pthread_t console_input_thread; | |
pthread_attr_t attr; | |
pthread_attr_init(&attr); | |
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); | |
pthread_create(&console_input_thread, NULL, consoleInputThread, NULL); | |
*/ | |
int i; | |
int j; | |
int box_width = 640/NUM_COLS; | |
int box_height = 480/NUM_ROWS; | |
int game_state_row[NUM_ROWS]; | |
int row_to_place[NUM_COLS]; | |
char go_first; | |
int player_move; | |
int valid_move; | |
int ai_move; | |
int game_over; | |
int winner; | |
char play_again; | |
do { | |
// Initialize the variables | |
valid_move = 1; | |
game_over = 0; | |
winner = -1; | |
// Draw Empty Board | |
for(i = 1; i<NUM_COLS; i++){ | |
VGA_Vline(i*box_width, 0, 480, dark_blue); | |
} | |
for(i = 1; i<NUM_ROWS; i++){ | |
VGA_Hline(0, i*box_height, 640, dark_blue); | |
} | |
for(i = 0; i<NUM_COLS; i++){ | |
for(j=0; j<NUM_ROWS; j++){ | |
VGA_disc(i*box_width+(box_width/2), j*box_height+(box_height/2), MIN(box_width, box_height)*0.40, black); | |
} | |
} | |
// Test Placing Discs | |
//placeDisc(2,3,1); | |
//placeDisc(6,5,2); | |
// Declare Initial Game States | |
for(i = 0; i < NUM_ROWS; i++) { | |
game_state_row[i] = 0; | |
} | |
// Declare Initial Row to Place | |
for(i = 0; i < NUM_COLS; i++) { | |
row_to_place[i] = 0; | |
} | |
// Ask if player wishes to go first | |
printf("Would you like to go first? [y/n]\n\r"); | |
scanf(" %c", &go_first); | |
// Player wishes to go first | |
if(go_first == 'y') { | |
printf("Which column would you like to place your token in? [1-7]\n\r"); | |
scanf("%d", &player_move); | |
player_move--; | |
// Check if invalid move, prompt for a valid move | |
if(row_to_place[player_move] >= NUM_ROWS || player_move<0 || player_move>NUM_COLS) { | |
valid_move = 0; | |
do { | |
printf("Invalid move! That column is already full.\n\r"); | |
printf("Which column would you like to place your token in? [1-7]\n\r"); | |
scanf("%d", &player_move); | |
player_move--; | |
if(row_to_place[player_move] < NUM_ROWS && player_move>-1 && player_move<NUM_COLS) valid_move = 1; | |
} while (!valid_move); | |
} | |
placeDisc(player_move, row_to_place[player_move], 2); | |
// Update game state and row tracker variables | |
updateGameState(game_state_row, row_to_place[player_move], player_move, 2); | |
row_to_place[player_move]++; | |
} | |
pullReset(); | |
// Loop forever, interfacing with SRAM to play a game | |
do { | |
// Send game states for all rows | |
//*(sram_ptr+1) = x1; | |
//*(sram_ptr+2) = x1; | |
//*(sram_ptr+3) = x1; | |
//*(sram_ptr+4) = x1; | |
//*(sram_ptr+5) = x1; | |
//*(sram_ptr+6) = x1; | |
for(i = 1; i < NUM_ROWS+1; i++) { | |
*(sram_ptr+i) = game_state_row[i-1]; | |
} | |
// Set the "data-ready" flag so the FPGA knows it's its turn to move (with an updated game state) | |
*(sram_ptr) = 1; | |
// Wait for the FPGA to zero the "data_ready" flag (meaning HPS's turn now) | |
printf("before while\n"); | |
while (*(sram_ptr)==1); | |
printf("after while\n"); | |
// Read the AI's move | |
ai_move = *(sram_ptr+NUM_ROWS+1); | |
printf("AI wants to place at column: %d\n", ai_move+1); | |
placeDisc(ai_move, row_to_place[ai_move], 1); | |
// Update game state and row tracker variables | |
updateGameState(game_state_row, row_to_place[ai_move], ai_move, 1); | |
row_to_place[ai_move]++; | |
//TODO: GAME OVER LOGIC BEGIN | |
game_over = checkGameOver(game_state_row, row_to_place, 1); | |
if (game_over == 1) { | |
winner = 1; | |
break; | |
} | |
else if (game_over == 2) { | |
winner = 0; | |
break; | |
} | |
else ; | |
//TODO: GAME OVER LOGIC END | |
// Player's Move | |
printf("Which column would you like to place your token in? [1-7]\n\r"); | |
scanf("%d", &player_move); | |
player_move--; | |
// Check if invalid move, prompt for a valid move | |
if(row_to_place[player_move] >= NUM_ROWS || player_move<0 || player_move>NUM_COLS) { | |
valid_move = 0; | |
do { | |
printf("Invalid move! That column is already full.\n\r"); | |
printf("Which column would you like to place your token in? [1-7]\n\r"); | |
scanf("%d", &player_move); | |
player_move--; | |
if(row_to_place[player_move] < NUM_ROWS && player_move>-1 && player_move<NUM_COLS) valid_move = 1; | |
} while (!valid_move); | |
} | |
placeDisc(player_move, row_to_place[player_move], 2); | |
// Update game state and row tracker variables | |
updateGameState(game_state_row, row_to_place[player_move], player_move, 2); | |
row_to_place[player_move]++; | |
//TODO: GAME OVER LOGIC BEGIN | |
game_over = checkGameOver(game_state_row, row_to_place, 2); | |
if (game_over == 1) { | |
winner = 2; | |
break; | |
} | |
else if (game_over == 2) { | |
winner = 0; | |
break; | |
} | |
else ; | |
//TODO: GAME OVER LOGIC END | |
} while (game_over == 0); // end while(1) | |
if(winner == 1) { | |
printf("Aw shucks, looks like the AI wins! Better luck next time!\n\r"); | |
} | |
else if(winner == 2) { | |
printf("Congratulations, you win! You beat the AI! A resume-worthy feat, if I do say so myself.\n\r"); | |
} | |
else if(winner == 0) { | |
printf("Phew, that was a tough one! Looks like you played the AI to a draw! Not bad, but you can do better.\n\r"); | |
} | |
else { | |
printf("Ruh roh. Something went wrong when setting the winner of the game. ERROR ERROR MISSION ABORT!\n\r"); | |
} | |
// Play again logic | |
printf("Would you like to play again? [y/n]\n\r"); | |
scanf(" %c", &play_again); | |
} while (play_again == 'y'); | |
} // end main | |
/**************************************************************************************** | |
* Subroutine to send a string of text to the VGA monitor | |
****************************************************************************************/ | |
void VGA_text(int x, int y, char *text_ptr) | |
{ | |
volatile char *character_buffer = (char *)vga_char_ptr; // VGA character buffer | |
int offset; | |
/* assume that the text string fits on one line */ | |
offset = (y << 7) + x; | |
while (*(text_ptr)) | |
{ | |
// write to the character buffer | |
*(character_buffer + offset) = *(text_ptr); | |
++text_ptr; | |
++offset; | |
} | |
} | |
/**************************************************************************************** | |
* Subroutine to clear text to the VGA monitor | |
****************************************************************************************/ | |
void VGA_text_clear() | |
{ | |
volatile char *character_buffer = (char *)vga_char_ptr; // VGA character buffer | |
int offset, x, y; | |
for (x = 0; x < 79; x++) | |
{ | |
for (y = 0; y < 59; y++) | |
{ | |
/* assume that the text string fits on one line */ | |
offset = (y << 7) + x; | |
// write to the character buffer | |
*(character_buffer + offset) = ' '; | |
} | |
} | |
} | |
/**************************************************************************************** | |
* Draw a filled rectangle on the VGA monitor | |
****************************************************************************************/ | |
#define SWAP(X, Y) \ | |
do \ | |
{ \ | |
int temp = X; \ | |
X = Y; \ | |
Y = temp; \ | |
} while (0) | |
void VGA_box(int x1, int y1, int x2, int y2, short pixel_color) | |
{ | |
char *pixel_ptr; | |
int row, col; | |
/* check and fix box coordinates to be valid */ | |
if (x1 > 639) | |
x1 = 639; | |
if (y1 > 479) | |
y1 = 479; | |
if (x2 > 639) | |
x2 = 639; | |
if (y2 > 479) | |
y2 = 479; | |
if (x1 < 0) | |
x1 = 0; | |
if (y1 < 0) | |
y1 = 0; | |
if (x2 < 0) | |
x2 = 0; | |
if (y2 < 0) | |
y2 = 0; | |
if (x1 > x2) | |
SWAP(x1, x2); | |
if (y1 > y2) | |
SWAP(y1, y2); | |
for (row = y1; row <= y2; row++) | |
for (col = x1; col <= x2; ++col) | |
{ | |
//640x480 | |
//pixel_ptr = (char *)vga_pixel_ptr + (row<<10) + col ; | |
// set pixel color | |
//*(char *)pixel_ptr = pixel_color; | |
VGA_PIXEL(col, row, pixel_color); | |
} | |
} | |
/**************************************************************************************** | |
* Draw a outline rectangle on the VGA monitor | |
****************************************************************************************/ | |
#define SWAP(X, Y) \ | |
do \ | |
{ \ | |
int temp = X; \ | |
X = Y; \ | |
Y = temp; \ | |
} while (0) | |
void VGA_rect(int x1, int y1, int x2, int y2, short pixel_color) | |
{ | |
char *pixel_ptr; | |
int row, col; | |
/* check and fix box coordinates to be valid */ | |
if (x1 > 639) | |
x1 = 639; | |
if (y1 > 479) | |
y1 = 479; | |
if (x2 > 639) | |
x2 = 639; | |
if (y2 > 479) | |
y2 = 479; | |
if (x1 < 0) | |
x1 = 0; | |
if (y1 < 0) | |
y1 = 0; | |
if (x2 < 0) | |
x2 = 0; | |
if (y2 < 0) | |
y2 = 0; | |
if (x1 > x2) | |
SWAP(x1, x2); | |
if (y1 > y2) | |
SWAP(y1, y2); | |
// left edge | |
col = x1; | |
for (row = y1; row <= y2; row++) | |
{ | |
//640x480 | |
//pixel_ptr = (char *)vga_pixel_ptr + (row<<10) + col ; | |
// set pixel color | |
//*(char *)pixel_ptr = pixel_color; | |
VGA_PIXEL(col, row, pixel_color); | |
} | |
// right edge | |
col = x2; | |
for (row = y1; row <= y2; row++) | |
{ | |
//640x480 | |
//pixel_ptr = (char *)vga_pixel_ptr + (row<<10) + col ; | |
// set pixel color | |
//*(char *)pixel_ptr = pixel_color; | |
VGA_PIXEL(col, row, pixel_color); | |
} | |
// top edge | |
row = y1; | |
for (col = x1; col <= x2; ++col) | |
{ | |
//640x480 | |
//pixel_ptr = (char *)vga_pixel_ptr + (row<<10) + col ; | |
// set pixel color | |
//*(char *)pixel_ptr = pixel_color; | |
VGA_PIXEL(col, row, pixel_color); | |
} | |
// bottom edge | |
row = y2; | |
for (col = x1; col <= x2; ++col) | |
{ | |
//640x480 | |
//pixel_ptr = (char *)vga_pixel_ptr + (row<<10) + col ; | |
// set pixel color | |
//*(char *)pixel_ptr = pixel_color; | |
VGA_PIXEL(col, row, pixel_color); | |
} | |
} | |
/**************************************************************************************** | |
* Draw a horixontal line on the VGA monitor | |
****************************************************************************************/ | |
#define SWAP(X, Y) \ | |
do \ | |
{ \ | |
int temp = X; \ | |
X = Y; \ | |
Y = temp; \ | |
} while (0) | |
void VGA_Hline(int x1, int y1, int x2, short pixel_color) | |
{ | |
char *pixel_ptr; | |
int row, col; | |
/* check and fix box coordinates to be valid */ | |
if (x1 > 639) | |
x1 = 639; | |
if (y1 > 479) | |
y1 = 479; | |
if (x2 > 639) | |
x2 = 639; | |
if (x1 < 0) | |
x1 = 0; | |
if (y1 < 0) | |
y1 = 0; | |
if (x2 < 0) | |
x2 = 0; | |
if (x1 > x2) | |
SWAP(x1, x2); | |
// line | |
row = y1; | |
for (col = x1; col <= x2; ++col) | |
{ | |
//640x480 | |
//pixel_ptr = (char *)vga_pixel_ptr + (row<<10) + col ; | |
// set pixel color | |
//*(char *)pixel_ptr = pixel_color; | |
VGA_PIXEL(col, row, pixel_color); | |
} | |
} | |
/**************************************************************************************** | |
* Draw a vertical line on the VGA monitor | |
****************************************************************************************/ | |
#define SWAP(X, Y) \ | |
do \ | |
{ \ | |
int temp = X; \ | |
X = Y; \ | |
Y = temp; \ | |
} while (0) | |
void VGA_Vline(int x1, int y1, int y2, short pixel_color) | |
{ | |
char *pixel_ptr; | |
int row, col; | |
/* check and fix box coordinates to be valid */ | |
if (x1 > 639) | |
x1 = 639; | |
if (y1 > 479) | |
y1 = 479; | |
if (y2 > 479) | |
y2 = 479; | |
if (x1 < 0) | |
x1 = 0; | |
if (y1 < 0) | |
y1 = 0; | |
if (y2 < 0) | |
y2 = 0; | |
if (y1 > y2) | |
SWAP(y1, y2); | |
// line | |
col = x1; | |
for (row = y1; row <= y2; row++) | |
{ | |
//640x480 | |
//pixel_ptr = (char *)vga_pixel_ptr + (row<<10) + col ; | |
// set pixel color | |
//*(char *)pixel_ptr = pixel_color; | |
VGA_PIXEL(col, row, pixel_color); | |
} | |
} | |
/**************************************************************************************** | |
* Draw a filled circle on the VGA monitor | |
****************************************************************************************/ | |
void VGA_disc(int x, int y, int r, short pixel_color) | |
{ | |
char *pixel_ptr; | |
int row, col, rsqr, xc, yc; | |
rsqr = r * r; | |
for (yc = -r; yc <= r; yc++) | |
for (xc = -r; xc <= r; xc++) | |
{ | |
col = xc; | |
row = yc; | |
// add the r to make the edge smoother | |
if (col * col + row * row <= rsqr + r) | |
{ | |
col += x; // add the center point | |
row += y; // add the center point | |
//check for valid 640x480 | |
if (col > 639) | |
col = 639; | |
if (row > 479) | |
row = 479; | |
if (col < 0) | |
col = 0; | |
if (row < 0) | |
row = 0; | |
//pixel_ptr = (char *)vga_pixel_ptr + (row<<10) + col ; | |
// set pixel color | |
//*(char *)pixel_ptr = pixel_color; | |
VGA_PIXEL(col, row, pixel_color); | |
} | |
} | |
} | |
/**************************************************************************************** | |
* Draw a circle on the VGA monitor | |
****************************************************************************************/ | |
void VGA_circle(int x, int y, int r, int pixel_color) | |
{ | |
char *pixel_ptr; | |
int row, col, rsqr, xc, yc; | |
int col1, row1; | |
rsqr = r * r; | |
for (yc = -r; yc <= r; yc++) | |
{ | |
//row = yc; | |
col1 = (int)sqrt((float)(rsqr + r - yc * yc)); | |
// right edge | |
col = col1 + x; // add the center point | |
row = yc + y; // add the center point | |
//check for valid 640x480 | |
if (col > 639) | |
col = 639; | |
if (row > 479) | |
row = 479; | |
if (col < 0) | |
col = 0; | |
if (row < 0) | |
row = 0; | |
//pixel_ptr = (char *)vga_pixel_ptr + (row<<10) + col ; | |
// set pixel color | |
//*(char *)pixel_ptr = pixel_color; | |
VGA_PIXEL(col, row, pixel_color); | |
// left edge | |
col = -col1 + x; // add the center point | |
//check for valid 640x480 | |
if (col > 639) | |
col = 639; | |
if (row > 479) | |
row = 479; | |
if (col < 0) | |
col = 0; | |
if (row < 0) | |
row = 0; | |
//pixel_ptr = (char *)vga_pixel_ptr + (row<<10) + col ; | |
// set pixel color | |
//*(char *)pixel_ptr = pixel_color; | |
VGA_PIXEL(col, row, pixel_color); | |
} | |
for (xc = -r; xc <= r; xc++) | |
{ | |
//row = yc; | |
row1 = (int)sqrt((float)(rsqr + r - xc * xc)); | |
// right edge | |
col = xc + x; // add the center point | |
row = row1 + y; // add the center point | |
//check for valid 640x480 | |
if (col > 639) | |
col = 639; | |
if (row > 479) | |
row = 479; | |
if (col < 0) | |
col = 0; | |
if (row < 0) | |
row = 0; | |
//pixel_ptr = (char *)vga_pixel_ptr + (row<<10) + col ; | |
// set pixel color | |
//*(char *)pixel_ptr = pixel_color; | |
VGA_PIXEL(col, row, pixel_color); | |
// left edge | |
row = -row1 + y; // add the center point | |
//check for valid 640x480 | |
if (col > 639) | |
col = 639; | |
if (row > 479) | |
row = 479; | |
if (col < 0) | |
col = 0; | |
if (row < 0) | |
row = 0; | |
//pixel_ptr = (char *)vga_pixel_ptr + (row<<10) + col ; | |
// set pixel color | |
//*(char *)pixel_ptr = pixel_color; | |
VGA_PIXEL(col, row, pixel_color); | |
} | |
} | |
// ============================================= | |
// === Draw a line | |
// ============================================= | |
//plot a line | |
//at x1,y1 to x2,y2 with color | |
//Code is from David Rodgers, | |
//"Procedural Elements of Computer Graphics",1985 | |
void VGA_line(int x1, int y1, int x2, int y2, short c) | |
{ | |
int e; | |
signed int dx, dy, j, temp; | |
signed int s1, s2, xchange; | |
signed int x, y; | |
char *pixel_ptr; | |
/* check and fix line coordinates to be valid */ | |
if (x1 > 639) | |
x1 = 639; | |
if (y1 > 479) | |
y1 = 479; | |
if (x2 > 639) | |
x2 = 639; | |
if (y2 > 479) | |
y2 = 479; | |
if (x1 < 0) | |
x1 = 0; | |
if (y1 < 0) | |
y1 = 0; | |
if (x2 < 0) | |
x2 = 0; | |
if (y2 < 0) | |
y2 = 0; | |
x = x1; | |
y = y1; | |
//take absolute value | |
if (x2 < x1) | |
{ | |
dx = x1 - x2; | |
s1 = -1; | |
} | |
else if (x2 == x1) | |
{ | |
dx = 0; | |
s1 = 0; | |
} | |
else | |
{ | |
dx = x2 - x1; | |
s1 = 1; | |
} | |
if (y2 < y1) | |
{ | |
dy = y1 - y2; | |
s2 = -1; | |
} | |
else if (y2 == y1) | |
{ | |
dy = 0; | |
s2 = 0; | |
} | |
else | |
{ | |
dy = y2 - y1; | |
s2 = 1; | |
} | |
xchange = 0; | |
if (dy > dx) | |
{ | |
temp = dx; | |
dx = dy; | |
dy = temp; | |
xchange = 1; | |
} | |
e = ((int)dy << 1) - dx; | |
for (j = 0; j <= dx; j++) | |
{ | |
//video_pt(x,y,c); //640x480 | |
//pixel_ptr = (char *)vga_pixel_ptr + (y<<10)+ x; | |
// set pixel color | |
//*(char *)pixel_ptr = c; | |
VGA_PIXEL(x, y, c); | |
if (e >= 0) | |
{ | |
if (xchange == 1) | |
x = x + s1; | |
else | |
y = y + s2; | |
e = e - ((int)dx << 1); | |
} | |
if (xchange == 1) | |
y = y + s2; | |
else | |
x = x + s1; | |
e = e + ((int)dy << 1); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment