diff --git a/Makefile b/Makefile index 5d395b3..5e2050f 100644 --- a/Makefile +++ b/Makefile @@ -1,23 +1,16 @@ SOURCEDIR = src DESTDIR = bin -CFLAGS = -std=c11 -Wno-return-local-addr +CFLAGS = -std=c11 -Wno-return-local-addr LIBS := $(shell ncursesw5-config --libs) -ltinfo -SRCS := $(wildcard $(SOURCEDIR)/*.c) -OBJS := $(patsubst %.c, %.o, $(SRCS)) +OBJS := $(patsubst %.c, %.o, $(wildcard $(SOURCEDIR)/*.c)) OUT = chess $(OUT): $(OBJS) - gcc -g $(OBJS) $(CFLAGS) -o $(DESTDIR)/$(OUT) - #NCURSES build - #gcc $(OBJS) $(CFLAGS) $(LIBS) $(shell ncursesw5-config --cflags) --enable-widec -o $(DESTDIR)/$(OUT) + gcc $(OBJS) $(CFLAGS) $(LIBS) $(shell ncursesw5-config --cflags) --enable-widec -o $(DESTDIR)/$(OUT) %.o: %.c gcc -c $< $(CFLAGS) -o $@ clean: rm $(OBJS) $(DESTDIR)/$(OUT) - -test: - $(foreach src, $(SRCS), gcc -c $(src) -DTEST $(CFLAGS) -o $(basename $(src)).o;) - gcc $(OBJS) $(CFLAGS) -o $(DESTDIR)/$(OUT) diff --git a/src/board.c b/src/board.c index a47234f..a437019 100644 --- a/src/board.c +++ b/src/board.c @@ -14,9 +14,9 @@ void PrintBoard(Board *b) { printf("%d ║", rank + 1); for (int file = 0; file < 8; ++file) { if(!b->board[file][rank]->hl){ - printf(" %lc ", GetUnicode(b->board[file][rank]->piece, b->board[file][rank]->color)); + printf(" %lc ", GetUnicode(b->board[file][rank], false)); }else{ - printf("<%lc>", GetUnicode(b->board[file][rank]->piece, b->board[file][rank]->color)); + printf("<%lc>", GetUnicode(b->board[file][rank], false)); } if (file != 7) printf("│"); } @@ -104,9 +104,9 @@ void FillBoardTest(Board *chessBoard) { //return the corresponding chess piece character (e.g. white pawn ♙ = 0x2659) given the color and piece type //note: unicode characters must be stored in the wchar_t type due to their larger size -wchar_t GetUnicode(EPieceType piece, EColor color) { - if (color == BLACK) { - switch (piece) { +wchar_t GetUnicode(Piece *p, bool whiteTile) { + if ((p->color == WHITE && whiteTile) || (p->color == BLACK && !whiteTile)) { + switch (p->piece) { case PAWN: return 0x2659; case ROOK: @@ -124,7 +124,7 @@ wchar_t GetUnicode(EPieceType piece, EColor color) { } } else { - switch (piece) { + switch (p->piece) { case PAWN: return 0x265F; //0x265F; case ROOK: diff --git a/src/board.h b/src/board.h index c920d14..b997799 100644 --- a/src/board.h +++ b/src/board.h @@ -14,6 +14,7 @@ typedef enum { } EPieceType; typedef enum { + PROGRESS, WHITE_WIN, BLACK_WIN, STALEMATE @@ -41,8 +42,8 @@ typedef struct { //captured board, black starts at 2*8=16 Piece *cap[4][8]; int nCapW, nCapB; - EColor p1; EColor currentPlayerTurn; + EColor p1; EGameState gameState; } Board; @@ -52,5 +53,5 @@ void FillBoard(Board *board); void FillBoardTest(Board *board); void CopyBoard(Board *b1, Board *b2); void DeleteBoard(Board *b); -wchar_t GetUnicode(EPieceType, EColor); +wchar_t GetUnicode(Piece *p, bool whiteTile); #endif diff --git a/src/game.c b/src/game.c index 4eb8b6e..a6d84b2 100644 --- a/src/game.c +++ b/src/game.c @@ -3,6 +3,7 @@ #include #include #include +#include const int VALID_MOVE_SIZE = 63; @@ -12,9 +13,9 @@ char *Move(int f0, int r0, int f1, int r1, Board *b) { if (IsValid(f0, r0, f1, r1, b)) { //-------- Update Misc. Board Data -------- //To count the total number of moves of each piece - b->board[f0][r0]->counter ++; + b->board[f0][r0]->counter++; - if(b->board[f0][r0]->color == BLACK){ + if(b->currentPlayerTurn == BLACK) { b->currentPlayerTurn = WHITE; }else{ b->currentPlayerTurn = BLACK; @@ -23,10 +24,46 @@ char *Move(int f0, int r0, int f1, int r1, Board *b) { //---------- Move the Piece ---------------- //store the moving piece temporarily Piece *p = b->board[f0][r0]; - + + //White Pawn En Passant + if (b->board[f1][r1-1]->piece == PAWN && (b->board[f0][r0]->color != b->board[f1][r1-1]->color) && b->board[f0][r0]->color == WHITE ) { + if(r0 == 4 && b->board[f1][r1-1]->counter == 1){ + b->board[f0][r0]->isCapturing = true; //so that CAN know that it is capturing + printf("%d\n", b->board[f0][r0]->isCapturing); + CAN(f0, r0, f1, r1, b); + printf("CAPTURING"); + Capture(f1, r1-1, b); + b->board[f1][r1] = p; + b->board[f0][r0] = malloc(sizeof(Piece)); + b->board[f0][r0]->piece = EMPTY; + b->board[f0][r0]->color = NO_COLOR; + b->board[f1][r1-1] = malloc(sizeof(Piece)); + b->board[f1][r1-1]->piece = EMPTY; + b->board[f1][r1-1]->color = NO_COLOR; + } + } + //Black Pawn En Passant + if (b->board[f1][r1+1]->piece == PAWN && (b->board[f0][r0]->color != b->board[f1][r1+1]->color) && b->board[f0][r0]->color == BLACK ) { + if(r0 == 3 && b->board[f1][r1+1]->counter == 1){ + b->board[f0][r0]->isCapturing = true; //so that CAN know that it is capturing + printf("%d\n", b->board[f0][r0]->isCapturing); + CAN(f0, r0, f1, r1, b); + + printf("CAPTURING"); + Capture(f1, r1 + 1, b); + b->board[f1][r1] = p; + b->board[f0][r0] = malloc(sizeof(Piece)); + b->board[f0][r0]->piece = EMPTY; + b->board[f0][r0]->color = NO_COLOR; + b->board[f1][r1+1] = malloc(sizeof(Piece)); + b->board[f1][r1+1]->piece = EMPTY; + b->board[f1][r1+1]->color = NO_COLOR; + } + } + //normal move if (b->board[f1][r1]->piece == EMPTY) { - CAN(f0, r0, f1, r1, b); + //CAN(f0, r0, f1, r1, b); b->board[f0][r0] = b->board[f1][r1]; b->board[f1][r1] = p; Castling (f0, r0, f1, r1, b); @@ -35,24 +72,22 @@ char *Move(int f0, int r0, int f1, int r1, Board *b) { //capturing move (prevent capturing king) else if (b->board[f1][r1]->piece != KING) { b->board[f0][r0]->isCapturing = true; //so that CAN know that it is capturing - printf("%d\n", b->board[f0][r0]->isCapturing); - CAN(f0, r0, f1, r1, b); + //printf("%d\n", b->board[f0][r0]->isCapturing); + //CAN(f0, r0, f1, r1, b); - printf("CAPTURING"); + //printf("CAPTURING"); Capture(f1, r1, b); b->board[f1][r1] = p; b->board[f0][r0] = malloc(sizeof(Piece)); b->board[f0][r0]->piece = EMPTY; b->board[f0][r0]->color = NO_COLOR; } - if (b->board[f0][r0]->piece == PAWN) printf("dOMOSF"); - //promotion (PAWN HAS BEEN MOVED) if (b->board[f1][r1]->piece == PAWN) { if ((r1 == 7 && b->board[f1][r1]->color == WHITE) || (r1 == 0 && b->board[f1][r1]->color == BLACK)) { char promoteTo; - printf("Please choose a piece to promote your pawn to: \nQ:Queen\nN:Knight\nB:Bishop\nR:Rook\nPiece:"); - scanf(" %c", &promoteTo); + //printf("Please choose a piece to promote your pawn to: \nQ:Queen\nN:Knight\nB:Bishop\nR:Rook\nPiece:"); + //scanf(" %c", &promoteTo); switch(promoteTo){ case 'B': b->board[f1][r1]->isPromoted = true; @@ -74,10 +109,11 @@ char *Move(int f0, int r0, int f1, int r1, Board *b) { } } } - + mvprintw(1,1, " "); }else{ - printf("Invalid move!\n"); + mvprintw(1,1,"INVALID MOVE!"); + //printf("Invalid move!\n"); } return ""; } @@ -321,6 +357,7 @@ LL *getValidMovesQueen(int f, int r, Board *b){ LL *out = malloc(sizeof(LL)); out->first = NULL; out->last = NULL; + //check vertical line up for(int i = r+1; i < 8; i++){ //b->board[f][i]->hl = 1; @@ -407,21 +444,21 @@ LL *getValidMovesQueen(int f, int r, Board *b){ } } //check diagonal bottom right - for(int i = f+1, j=r-1; i<8 && j>=0; i++,j--){ - //b->board[i][j]->hl = 1; - if(b->board[i][j]->color != b->board[f][r]->color){ + for (int i = f+1, j= r-1; i < 8 && j>=0; i++, j--){ + if (b->board[f][r]->color != b->board[i][j]->color){ MOVE *curr = malloc(sizeof(MOVE)); - curr->r0 = r; + curr->r0 = r; curr->f0 = f; curr->r1 = j; curr->f1 = i; Append(out, curr); - if(b->board[i][j]->color != EMPTY){ + //b->board[i][j]->hl = 1; + if (b->board[i][j]->piece != EMPTY){ break; } - }else{ - break; } + + else break; } //check diagonal bottom left for(int i = f-1, j=r-1; i>=0 && j>=0; i--,j--){ @@ -844,6 +881,34 @@ LL *getValidMovesPawn(int f0, int r0, Board *b){ Append(out, curr); } //} + + //EN PASSANT for WHITE PAWN + f = f0+1, r = r0+1; + if (f < 8 && r < 8){ + if(b->board[f0+1][r0]->piece == PAWN && b->board[f0+1][r0]->color == BLACK){ + if(r0 == 4 && b->board[f0+1][r0]->counter == 1){ + MOVE *curr = malloc(sizeof(MOVE)); + curr -> f0 = f0; + curr -> r0 = r0; + curr -> f1 = f; + curr -> r1 = r; + Append(out, curr); + } + } + } + f = f0-1, r = r0+1; + if (f >= 0 && r < 8){ + if(b->board[f0-1][r0]->piece == PAWN && b->board[f0-1][r0]->color == BLACK){ + if(r0 == 4 && b->board[f0-1][r0]->counter == 1){ + MOVE *curr = malloc(sizeof(MOVE)); + curr -> f0 = f0; + curr -> r0 = r0; + curr -> f1 = f; + curr -> r1 = r; + Append(out, curr); + } + } + } //check to see if white pawn can capture left diagonally f = f0 - 1, r = r0 + 1; //left diagonal //if(!IsInCheck(f0, r0, f, r, b)){ //check to see if move to be made will put King in check @@ -901,7 +966,34 @@ LL *getValidMovesPawn(int f0, int r0, Board *b){ curr -> r1 = r; Append(out, curr); } - //PROMOTION FOR BLACK PAWN + //EN PASSANT for BLACK PAWN + f = f0-1, r = r0-1; + if (f >= 0 && r >= 0){ + if(b->board[f0-1][r0]->piece == PAWN && b->board[f0-1][r0]->color == WHITE){ + + if(r0 == 3 && b->board[f0-1][r0]->counter == 1){ + MOVE *curr = malloc(sizeof(MOVE)); + curr -> f0 = f0; + curr -> r0 = r0; + curr -> f1 = f; + curr -> r1 = r; + Append(out, curr); + } + } + } + f = f0+1, r = r0-1; + if (f < 8 && r >= 0){ + if(b->board[f0+1][r0]->piece == PAWN && b->board[f0+1][r0]->color == WHITE){ + if(r0 == 3 && b->board[f0+1][r0]->counter == 1){ + MOVE *curr = malloc(sizeof(MOVE)); + curr -> f0 = f0; + curr -> r0 = r0; + curr -> f1 = f; + curr -> r1 = r; + Append(out, curr); + } + } + } } return out; } @@ -1007,6 +1099,13 @@ LL *getValidMoves(Board * board, EColor player){ return out; } +bool IsMated(Board *b, EColor color) { + LL *m = getValidMoves(b, color); + bool mated = (m->first == NULL); + DeleteList(m); + return mated; +} + void Castling(int f0, int r0, int f1, int r1, Board *b) { if (f1 == 6 && r1 == 0 && f0 == 4 && r0 == 0) @@ -1114,7 +1213,7 @@ int EvaluateBoard(Board *b){ } return value; } - +/* MOVE *IdealMove(Board *b, EColor player){ LL *moves; int score = EvaluateBoard(b); @@ -1126,7 +1225,6 @@ MOVE *IdealMove(Board *b, EColor player){ DeleteBoard(b2); return best; } -/* void GenerateTree(Board *source, TREE *out, int depth){ MOVE * valid[VALID_MOVE_SIZE]; @@ -1167,6 +1265,29 @@ void GenerateTree(Board *source, TREE *out, int depth){ } */ +MOVE *IdealMove(Board *b, EColor color) { + srand(time(NULL)); + + LL *m = getValidMoves(b, color); + int size = GetListSize(m); + int n = rand() % size; + + //iterate through n elements of the moves list + LLElem *curr = m->first; + for (; n >= 0; --n) { + curr = curr->next; + } + + //deep copy move to allow freeing + MOVE *move = malloc(sizeof(MOVE)); + move->f0 = ((MOVE *)curr->data)->f0; + move->r0 = ((MOVE *)curr->data)->r0; + move->f1 = ((MOVE *)curr->data)->f1; + move->r1 = ((MOVE *)curr->data)->r1; + + DeleteList(m); + return move; +} @@ -1342,5 +1463,17 @@ void DeleteList(LL *list){ curr = next; } free(list); + list = NULL; } +int GetListSize(LL *list) { + int i = 0; + LLElem *curr = list->first; + + while (curr->next) { + i++; + curr = curr->next; + } + + return i; +} diff --git a/src/game.h b/src/game.h index 408f522..cc75c1e 100644 --- a/src/game.h +++ b/src/game.h @@ -51,7 +51,7 @@ char *Move(int f0, int r0, int f1, int r1, Board *b); void RawMove(int f0, int r0, int f1, int r1, Board *b); bool IsValid(int f0, int r0, int f1, int r1, Board *b); bool IsInCheck(int f0, int r0, int f1, int r1, Board *b); -bool IsMated(int f0, int r0, int f1, int r1, Board *b); +bool IsMated(Board *b, EColor color); void Capture(int f, int r, Board *b); void Promote(int f0, int r0, Board *b); void Castling(int f0, int r0, int f1, int r1, Board *b); @@ -78,4 +78,5 @@ MOVE *IdealMove(Board *b, EColor player); void Append(LL *list, void *data); void DeleteList(LL *list); +int GetListSize(LL *list); #endif diff --git a/src/gui.c b/src/gui.c new file mode 100644 index 0000000..c94a41b --- /dev/null +++ b/src/gui.c @@ -0,0 +1,459 @@ +#include "gui.h" + +const int MIN_X = 0, MIN_Y = 0; + +//start ncurses mode, returns false if window size is smaller than chess board +bool InitGUI(GUI *g) { + g->state = MENU; + + initscr(); + curs_set(0); + noecho(); + cbreak(); + + //weird stuff to make mouse work + keypad(stdscr, true); + printf("\033[?1002h\n"); //xterm report mouse button events (NO MOVEMENT EVENTS) + + //set up mouse event + mousemask(BUTTON1_PRESSED, NULL); + + getmaxyx(stdscr, g->y, g->x); + wattrset(stdscr, A_NORMAL); + + //setup menu and options + g->menu = malloc(sizeof(Menu)); + g->game = malloc(sizeof(Game)); + g->menu->size = 3; + g->menu->settings = calloc(3, sizeof(MenuSetting *)); + + //GAMEMODE OPTIONS + g->menu->settings[0] = malloc(sizeof(MenuSetting)); + g->menu->gamemode = g->menu->settings[0]; + g->menu->settings[0]->text = "GAMEMODE:"; + g->menu->settings[0]->size = 2; + g->menu->settings[0]->selected = 0; + // allocate space + g->menu->settings[0]->options = calloc(2, sizeof(MenuOption *)); + g->menu->settings[0]->options[0] = malloc(sizeof(MenuOption)); + g->menu->settings[0]->options[1] = malloc(sizeof(MenuOption)); + // option0, tutorial + g->menu->settings[0]->options[0]->text = "PLAYER VS PLAYER"; + g->menu->settings[0]->options[0]->value = 0; + // option1, player vs ai + g->menu->settings[0]->options[1]->text = "PLAYER VS AI"; + g->menu->settings[0]->options[1]->value = 1; + SetBox(g->menu->settings[0], g->y, g->x, 4); + + + //DIFFICULTY OPTIONS + g->menu->settings[1] = malloc(sizeof(MenuSetting)); + g->menu->difficulty = g->menu->settings[1]; + g->menu->settings[1]->text = "DIFFICULTY:"; + g->menu->settings[1]->size = 3; + g->menu->settings[1]->selected = 0; + // allocate space + g->menu->settings[1]->options = calloc(3, sizeof(MenuOption *)); + g->menu->settings[1]->options[0] = malloc(sizeof(MenuOption)); + g->menu->settings[1]->options[1] = malloc(sizeof(MenuOption)); + g->menu->settings[1]->options[2] = malloc(sizeof(MenuOption)); + // option0, tutorial + g->menu->settings[1]->options[0]->text = "BEGINNER"; + g->menu->settings[1]->options[0]->value = 0; + // option1, player vs ai + g->menu->settings[1]->options[1]->text = "INTERMEDIATE"; + g->menu->settings[1]->options[1]->value = 1; + // option2 + g->menu->settings[1]->options[2]->text = "EXPERT"; + g->menu->settings[1]->options[2]->value = 2; + SetBox(g->menu->settings[1], g->y, g->x, 6); + + //PLAYER COLOR + g->menu->settings[2] = malloc(sizeof(MenuSetting)); + g->menu->playerColor = g->menu->settings[2]; + g->menu->settings[2]->text = "COLOR:"; + g->menu->settings[2]->size = 2; + g->menu->settings[2]->selected = 0; + // allocate space + g->menu->settings[2]->options = calloc(2, sizeof(MenuOption *)); + g->menu->settings[2]->options[0] = malloc(sizeof(MenuOption)); + g->menu->settings[2]->options[1] = malloc(sizeof(MenuOption)); + // option0, tutorial + g->menu->settings[2]->options[0]->text = "WHITE"; + g->menu->settings[2]->options[0]->value = 0; + // option1, player vs ai + g->menu->settings[2]->options[1]->text = "BLACK"; + g->menu->settings[2]->options[1]->value = 1; + SetBox(g->menu->settings[2], g->y, g->x, 8); + + //START BUTTON + g->menu->start.x0 = (g->x - 10)/2; + g->menu->start.x1 = g->menu->start.x0 + 9; + g->menu->start.y1 = g->menu->start.y0 = g->y - 5; + + //EXIT BUTTON + g->menu->exit.x0 = (g->x - 4)/2; + g->menu->exit.x1 = g->menu->exit.x0 + 3; + g->menu->exit.y1 = g->menu->exit.y0 = g->y - 3; + + //Setup game + g->game->board = malloc(sizeof(Board)); + FillBoard(g->game->board); + g->game->InitMove = false; + + //EXIT BUTTON + g->game->exit.x0 = (g->x - 4)/2; + g->game->exit.x1 = g->game->exit.x0 + 3; + g->game->exit.y1 = g->game->exit.y0 = g->y - 3; + + if (g->y < MIN_Y || g->x < MIN_X) return false; + else return true; +} + + + +//frees GUI structs and deconstructs ncurses +void Cleanup(GUI *g) { + echo(); + nocbreak(); + endwin(); + printf("\033[?1002l\n"); //disable mouse reporting + //ughhhhhh pointers + + //free menu and children + for (int i = 0; i < g->menu->size; ++i) { + MenuSetting *curSet = g->menu->settings[i]; + for (int j = 0; j < curSet->size; ++j) { + free(curSet->options[j]); + } + free(curSet->options); + free(curSet); + } + free(g->menu->settings); + free(g->menu); + + //free and unset gui pointer + free(g); + g = NULL; + + system("clear"); + //raise(SIGINT); +} + + + +//to be called in main game loop, handles the entire GUI operation +void DoGUI(GUI *g) { + //handle mouse input + MEVENT e; + int ch = wgetch(stdscr); + + if (ch == KEY_MOUSE) { + if (getmouse(&e) == OK) { + if (e.bstate & (BUTTON1_PRESSED)) { + HandleMouse(g, e); + } + } + } + + flushinp(); + + //handle gui drawing + switch (g->state) { + case MENU: + DrawMenu(g); + break; + case GAME: + DrawGame(g); + break; + } +} + + + +//draws and handles the menu +void DrawMenu(GUI *g) { + char *title = "QUEEN'S CHESS"; + int titleX = (g->x - strlen(title)) / 2; + + //draw the top border around the title + mvhwall(0, 0 , 0x250C, 0x2500, 0x2500, titleX - 1); + mvhwall(0, titleX + strlen(title) + 1 , 0x2500, 0x2500, 0x2510, g->x - (titleX + strlen(title) + 1)); + + //draw the title + attron(A_BOLD); + mvprintw(0, titleX, title); + attroff(A_BOLD); + + //draw the side vertical borders + mvvwall(1, 0, 0x2502, g->y - 2); + mvvwall(1, g->x - 1, 0x2502, g->y - 2); + + //draw the bottom border + mvhwall(g->y - 1, 0 , 0x2514, 0x2500, 0x2518, g->x); + + //iterate over every setting, print the label + for (int s = 0; s < g->menu->size; ++s) { + mvprintw(g->menu->settings[s]->labelY, g->menu->settings[s]->labelX, g->menu->settings[s]->text); + //iterater over every option of the setting, printing each + for (int o = 0; o < g->menu->settings[s]->size; ++o) { + if (g->menu->settings[s]->selected == o) attron(A_BOLD); + mvprintw(g->menu->settings[s]->options[o]->bound.y0, g->menu->settings[s]->options[o]->bound.x0, g->menu->settings[s]->options[o]->text); + attroff(A_BOLD); + } + } + + //print the start button + mvprintw(g->menu->start.y0, g->menu->start.x0, "START GAME"); + + //print the quit button + mvprintw(g->menu->exit.y0, g->menu->exit.x0, "QUIT"); + + refresh(); +} + + + +void DrawGame(GUI *g) { + char *title = "QUEEN'S CHESS"; + int titleX = (g->x - strlen(title)) / 2; + + //draw the top border around the title + mvhwall(0, 0 , 0x250C, 0x2500, 0x2500, titleX - 1); + mvhwall(0, titleX + strlen(title) + 1 , 0x2500, 0x2500, 0x2510, g->x - (titleX + strlen(title) + 1)); + + //draw the title + attron(A_BOLD); + mvprintw(0, titleX, title); + attroff(A_BOLD); + + //draw the side vertical borders + mvvwall(1, 0, 0x2502, g->y - 2); + mvvwall(1, g->x - 1, 0x2502, g->y - 2); + + //draw the bottom border + mvhwall(g->y - 1, 0 , 0x2514, 0x2500, 0x2518, g->x); + + //setup chess board window + //36w 18h + g->game->boardBound.y0 = (g->y - 18)/2; + g->game->boardBound.x0 = (g->x - 36)/2; + g->game->boardBound.y1 = g->game->boardBound.y0 + 18; + g->game->boardBound.x1 = g->game->boardBound.x0 + 36; + + //create the board window + g->game->bWin = newwin(18, 36, g->game->boardBound.y0, g->game->boardBound.x0); + + //AI Move + EColor AIc = (g->game->board->p1 == WHITE) ? BLACK : WHITE; + + if (g->game->board->currentPlayerTurn == AIc) { + MOVE *m = IdealMove(g->game->board, AIc); + Move(m->f0, m->r0, m->f1, m->r1, g->game->board); + } + + //print turn + if (g->game->board->currentPlayerTurn == WHITE) attron(A_BOLD); + mvprintw(g->game->boardBound.y0 - 1, g->game->boardBound.x0 + 3, "WHITE"); + attroff(A_BOLD); + + if (g->game->board->currentPlayerTurn == BLACK) attron(A_BOLD); + mvprintw(g->game->boardBound.y0 - 1, g->game->boardBound.x1 - 5, "BLACK"); + attroff(A_BOLD); + + //print any win conditions + if (IsMated(g->game->board, WHITE)) + mvprintw(g->game->boardBound.y0 - 2, (g->x - 9) / 2,"BLACK WINS"); + if (IsMated(g->game->board, BLACK)) + mvprintw(g->game->boardBound.y0 - 2, (g->x - 9) / 2,"WHITE WINS"); + + //begin printing board + bool whiteTile; + mvwaddstr(g->game->bWin, 0, 3, "╔═══╤═══╤═══╤═══╤═══╤═══╤═══╤═══╗"); + + for (int y = 0; y < 8; ++y) { + int rank = (g->game->board->p1 == BLACK) ? y : (7 - y); + mvwprintw(g->game->bWin, 1 + y * 2, 0, " %d ║ │ │ │ │ │ │ │ ║", rank + 1); + mvwaddstr(g->game->bWin, 2 + y * 2, 0, " ╟───┼───┼───┼───┼───┼───┼───┼───╢"); + for (int x = 0; x <= 7; ++x) { + int file = (g->game->board->p1 == BLACK) ? (7 - x) : x; + whiteTile = ((rank % 2) && !(file % 2)) || (!(rank % 2) && (file % 2)); + if (whiteTile) { + //print tile characters + mvwprintw(g->game->bWin, 1 + y * 2, 4 + x * 4, "%s","█ █"); + wattron(g->game->bWin, A_STANDOUT); + } + + mvwprintw(g->game->bWin, 1 + y * 2, 5 + x * 4, "%lc", GetUnicode(g->game->board->board[file][rank], whiteTile)); + wattroff(g->game->bWin, A_STANDOUT); + + if (rank == 0) { + mvwprintw(g->game->bWin, 17, 5 + x * 4, "%c", 65 + file); + } + } + } + mvwaddstr(g->game->bWin, 16, 3, "╚═══╧═══╧═══╧═══╧═══╧═══╧═══╧═══╝"); + + + //print the quit button + mvprintw(g->game->exit.y0, g->game->exit.x0, "QUIT"); + + refresh(); + wrefresh(g->game->bWin); +} + + + +void HandleMouse(GUI *g, MEVENT e) { + switch(g->state) { + case MENU: + //iterate over every setting, print the label + for (int s = 0; s < g->menu->size; ++s) { + mvprintw(g->menu->settings[s]->labelY, g->menu->settings[s]->labelX, g->menu->settings[s]->text); + //iterater over every option of the setting, printing each + for (int o = 0; o < g->menu->settings[s]->size; ++o) { + if (IsInBox(e.y, e.x, g->menu->settings[s]->options[o]->bound)) + g->menu->settings[s]->selected = o; + } + } + bool invalid = false; + + if (IsInBox(e.y, e.x, g->menu->start)) { + for (int i = 0; i < g->menu->size; ++i) { + if (g->menu->settings[i]->selected == -1) { + mvprintw(3, 3, "Invalid Settings!"); + invalid = true; + } + } + if (!invalid) { + switch (g->menu->playerColor->selected) { + case 0: + g->game->board->p1 = WHITE; + break; + case 1: + g->game->board->p1 = BLACK; + break; + } + clear(); + g->state = GAME; + } + } + + if (IsInBox(e.y, e.x, g->menu->exit)) g->state = EXITING; + break; + case GAME: + //check if chess board interacted with + if (IsInBox(e.y, e.x, g->game->boardBound)) { + int x, y; + WinToBoard(g, e.y, e.x, &y, &x); + if (x != -1 && y != -1) { + if (!g->game->InitMove) { + if (g->menu->gamemode->selected == 0) { + if (g->game->board->board[x][y]->piece != EMPTY && g->game->board->currentPlayerTurn == g->game->board->board[x][y]->color) { + g->game->iY = y; + g->game->iX = x; + g->game->InitMove = true; + } + } + else { + if (g->game->board->board[x][y]->piece != EMPTY && g->game->board->currentPlayerTurn == g->game->board->p1 && g->game->board->p1 == g->game->board->board[x][y]->color) { + g->game->iY = y; + g->game->iX = x; + g->game->InitMove = true; + } + } + + } + else { + Move(g->game->iX, g->game->iY, x, y, g->game->board); + g->game->InitMove = false; + } + + } + } + if (IsInBox(e.y, e.x, g->menu->exit)) g->state = EXITING; + break; + } + +} + + + +//moves the cursor and draws a horizontal wall with the specified characters +void mvhwall(int y, int x, wchar_t start, wchar_t mid, wchar_t end, int n) { + if (n >= 2) { + wchar_t *c = malloc(sizeof(wchar_t) * (n + 1)); + + c[0] = start; + c[n - 1] = end; + for (int i = 1; i < n - 1; ++i) { + c[i] = mid; + } + c[n] = L'\0'; + + mvaddwstr(y, x, c); + free(c); + } +} + + + +//moves the cursor and draws a vertical wall with the specified character +void mvvwall(int y, int x, wchar_t w, int n) { + cchar_t *c = malloc(sizeof(cchar_t)); + c->chars[0] = w; + c->chars[1] = L'\0'; + c->attr = A_NORMAL; + + for (int i = 0; i < n; ++i) mvadd_wch(y + i, x, c); + free(c); +} + + + +bool IsInBox(int y, int x, Box b) { + return y >= b.y0 && y <= b.y1 && x >= b.x0 && x <= b.x1; +} + + + +void SetBox(MenuSetting *s, int height, int width, int y) { + s->labelX = width - strlen(s->text); + + //calculate left margin for centering, 4 char spacing + for (int i = 0; i < s->size; ++i) { + s->labelX -= 4 + strlen(s->options[i]->text); + } + + s->labelX = s->labelX / 2; + s->labelY = y; + + s->options[0]->bound.x0 = s->labelX + strlen(s->text) + 4; + s->options[0]->bound.y0 = y; + s->options[0]->bound.x1 = s->options[0]->bound.x0 + strlen(s->options[0]->text) - 1; + s->options[0]->bound.y1 = y; + for (int i = 1; i < s->size; ++i) { + s->options[i]->bound.x0 = s->options[i-1]->bound.x0 + strlen(s->options[i-1]->text) + 4; + s->options[i]->bound.y0 = y; + s->options[i]->bound.x1 = s->options[i]->bound.x0 + strlen(s->options[i]->text) - 1; + s->options[i]->bound.y1 = y; + } +} + + +//convert main window space to board +void WinToBoard(GUI *g, int yw, int xw, int *yt, int *xt) { + int x = xw, y = yw, rank, file; + + //set x and y to board coordinates + y -= g->game->boardBound.y0 + 1; + x -= g->game->boardBound.x0 + 4; + + //>= needs to be changed to + if (y % 2 >= 0 && y / 2 <= 7) *yt = (g->game->board->p1 == BLACK) ? (y / 2) : (7 - (y / 2)); + else *yt = -1; + if (x % 4 < 3 && x / 4 <= 7) *xt = (g->game->board->p1 == BLACK) ? (7 - (x / 4)) : (x / 4); + else *xt = -1; + +} + diff --git a/src/gui.h b/src/gui.h new file mode 100644 index 0000000..e612756 --- /dev/null +++ b/src/gui.h @@ -0,0 +1,83 @@ +#ifndef GUI_H +#define GUI_H +#define _XOPEN_SOURCE_EXTENDED 1 +#define NCURSES_WIDECHAR 1 + +#include +#include +#include +#include +#include "board.h" +#include "game.h" + +extern const int MIN_X, MIN_Y; + +typedef enum { + MENU, + GAME, + EXITING +} EGUIState; + +typedef struct { + int x0, x1; + int y0, y1; +} Box; + +typedef struct { + Box bound; + int value; + char *text; +} MenuOption; + +typedef struct { + MenuOption **options; + int selected; + char *text; + int labelX, labelY; + size_t size; +} MenuSetting; + +typedef struct { + //MenuSetting represents [text | MenuOption[0] | ... | MenuOption[size - 1]] + MenuSetting *gamemode; + MenuSetting *difficulty; + MenuSetting *playerColor; + + size_t size; + MenuSetting **settings; + Box start; + Box exit; +} Menu; + +typedef struct { + //board window + WINDOW *bWin; + Box boardBound; + bool InitMove; + int iX, iY; + + Board *board; + Box exit; + EGameState state; +} Game; + +typedef struct { + Menu *menu; + Game *game; + EGUIState state; + int y, x; +} GUI; + +bool IsInBox(int y, int x, Box b); +void SetBox(MenuSetting *, int, int, int); +bool InitGUI(GUI *g); +void DoGUI(GUI *g); +void DrawMenu(GUI *g); +void DrawGame(GUI *g); +void HandleMouse(GUI *g, MEVENT e); +void Cleanup(GUI *g); +void WinToBoard(GUI *g, int yw, int xw, int *yt, int *xt); + +void mvhwall(int, int, wchar_t, wchar_t, wchar_t, int); +void mvvwall(int, int, wchar_t, int); +#endif diff --git a/src/main.c b/src/main.c index 5df669d..6aca28a 100644 --- a/src/main.c +++ b/src/main.c @@ -1,137 +1,27 @@ #include #include #include +#include #include "board.h" #include "game.h" +#include "gui.h" int main(int argc, char *argv[]) { - setlocale(LC_CTYPE,""); - Board *chessBoard = malloc(sizeof(Board)); - //Board *chessBoardCopy = malloc(sizeof(Board)); - int a; - printf("Select 1 for White and 2 for Black and 3 for Free Play: "); - scanf("%d",&a); - FillBoard(chessBoard); - PrintBoard(chessBoard); + setlocale(LC_ALL, ""); - //If side chosen is White - if (a == 1) - { - bool done = false; - while (!done){ - printf("Select a piece to move in the form \"FR FR\" (e.g. A2 A3): "); - char s0[3], s1[3]; - char *MoveCheck; - scanf("%2s %2s", s0, s1); + GUI *gui = malloc(sizeof(GUI)); - if (chessBoard->board[s0[0] - 65][s0[1] - 49]->color == BLACK) - { - printf("Invalid move! Choose a White piece!\n"); - continue; - } - - Move(s0[0] - 65, s0[1] - 49, s1[0] - 65, s1[1] - 49, chessBoard); + InitGUI(gui); + DrawMenu(gui); + bool done = false; - //Implement a Move check function which checks if the - //previous move by the player was valid (To Be Done) - //if (MoveCheck == 0) - //{ - // continue; - //} - - MoveRandomBlack(chessBoard); - PrintBoard(chessBoard); - - } - } - - //If side chosen is Black - if (a == 2) - { - bool done = false; - while (!done){ - printf("Select a piece to move in the form \"FR FR\" (e.g. A2 A3): "); - char s0[3], s1[3]; - char *MoveCheck; - scanf("%2s %2s", s0, s1); - - if (chessBoard->board[s0[0] - 65][s0[1] - 49]->color == WHITE) - { - printf("Invalid move! Choose a Black piece!\n"); - continue; - } - - Move(s0[0] - 65, s0[1] - 49, s1[0] - 65, s1[1] - 49, chessBoard); - - //Implement a Move check function which checks if the - //previous move by the player was valid (To Be Done) - //if (MoveCheck == 0) - //{ - // continue; - //} - - MoveRandomWhite(chessBoard); - PrintBoard(chessBoard); - - } - } - - if (a == 3) - { - bool done = false; - while (!done) - { - printf("Select a piece to move in the form \"FR FR\" (e.g. A2 A3): "); - char s0[3], s1[3]; - scanf("%2s %2s", s0, s1); - //MOVE *moves[63]; - #ifdef TEST - printf("Valid: %d | Move: %s %s", IsValid(s0[0] - 65, s0[1] - 49, s1[0] - 65, s1[1] - 49, chessBoard), s0, s1); - #else - Move(s0[0] - 65, s0[1] - 49, s1[0] - 65, s1[1] - 49, chessBoard); - //CopyBoard(chessBoard, chessBoardCopy); - //MOVE *best = IdealMove(chessBoard, WHITE); - //printf("IdealMove: %d%d %d%d", best->f0, best->r0, best->f1, best->r1); - - #endif - - PrintBoard(chessBoard); - } - } - //dev only - if(a == 4){ - while (true) - { - printf("Select a piece to move in the form \"FR FR\" (e.g. A2 A3): "); - char s0[3], s1[3]; - scanf("%2s %2s", s0, s1); - /* - LL *list = malloc(sizeof(LL)); - list->first = NULL; - list->last = NULL; - MOVE *m = malloc(sizeof(MOVE)); - m->f1 = 1; - m->r1 = 1; - m->f0 = 1; - m->r0 = 1; - Append(list, m); - printf("LIST ELEMENT: %d", ((MOVE *)(list->first->data))->f1); - */ - Move(s0[0] - 65, s0[1] - 49, s1[0] - 65, s1[1] - 49, chessBoard); - LL *validlist = getValidMoves(chessBoard, WHITE); - LLElem *curr = validlist->first; - int count = 0; - while(curr){ - MOVE *currmove = (MOVE *)(curr->data); - printf("MOVE#%d: %c%c->%c%c\n", ++count, currmove->f0 + 65, currmove->r0 + 49, currmove->f1 + 65, currmove->r1 + 49); - curr = curr->next; - } - PrintBoard(chessBoard); - } + while (!done) { + DoGUI(gui); + if (gui->state == EXITING) done = true; } - - + + Cleanup(gui); return 0; }