diff --git a/README.md b/README.md index ba3fd52..6abf6df 100644 --- a/README.md +++ b/README.md @@ -2,4 +2,7 @@ AnnaConnect is a silly little text-based connect 4 game c: -Supports vt100 escape codes and ncurses for drawing the UI \ No newline at end of file +Supports vt100 escape codes and ncurses for drawing the UI + +Stuff still be changing so fast that the code is the documentation, +but at least it's got good enough comments methinks diff --git a/config.h b/config.h index f1e8451..a134cab 100644 --- a/config.h +++ b/config.h @@ -5,5 +5,3 @@ #define BOARD_HEIGHT 6 #define FIRST_NUMBER 1 - -#define ARROWS diff --git a/logic.c b/logic.c index 43e192e..aa36dff 100644 --- a/logic.c +++ b/logic.c @@ -17,40 +17,33 @@ * along with this program. If not, see . */ #include "logic.h" +#include "macros.h" -static inline int top( const int a, const int b ){ - return a < b ? b : a; -} - -static inline int bottom( const int a, const int b ){ - return a < b ? a : b; -} - -static inline int heightMask( const int a ){ +static column_t heightMask( const rowsint_t a ){ return ( 1 << a ) - 1; } -static inline int safeHeightMask( const int a ){ +static column_t safeHeightMask( const rowsint_t a ){ return a > 0 ? heightMask( a ) : 0; } -static inline int bottomHeightMask( const int a, const int b ){ +static column_t bottomHeightMask( const rowsint_t a, const rowsint_t b ){ return safeHeightMask( bottom( a, b ) ); } -inline void playMove( +void playMove( board_t *boardptr, - const int column + const columnsint_t column ){ boardptr->column [ column ] |= boardptr->player << boardptr->height [ column ]; boardptr->height [ column ]++; } -inline void calcWins( +void calcWins( wins_t *wins, const board_t board, - const int column + const columnsint_t column ){ // First the simplest win, the humble tower // Check for lil towers @@ -79,7 +72,7 @@ inline void calcWins( // Now the rest of the wins // First connect 2 for( - int i = top( column - 1, 0 ); + columnsint_t i = clipped_subtract( column, 1 ); i < bottom( column + 1, board.columns - 1 ); i++ ){ @@ -108,7 +101,7 @@ inline void calcWins( } // Then stitch the twos together and count for( - int i = top( column - 3, 0 ); + columnsint_t i = clipped_subtract( column, 3 ); i < bottom( column + 1, board.columns - 3 ); i++ ){ diff --git a/logic.h b/logic.h index 4123f8a..488894c 100644 --- a/logic.h +++ b/logic.h @@ -6,11 +6,11 @@ extern void playMove( board_t *boardptr, - const int column + const columnsint_t column ); extern void calcWins( wins_t *wins, const board_t board, - const int column + const columnsint_t column ); diff --git a/macros.h b/macros.h index a364109..20b6cf4 100644 --- a/macros.h +++ b/macros.h @@ -1,5 +1,9 @@ /* SPDX-License-Identifier: GPL-2.0-only */ #pragma once +#define top( a, b ) ( a < b ? b : a ) +#define bottom( a, b ) ( a < b ? a : b ) +#define clipped_subtract( a, b ) ( a < b ? 0 : a - b ) + #define XSTR(s) STR(s) #define STR(s) #s diff --git a/makefile b/makefile index f08e653..5d10e86 100644 --- a/makefile +++ b/makefile @@ -1,27 +1,46 @@ #!/usr/bin/make -f + +# Target architecture for compilation +ARCH = native + +# Optimization level +O = 3 + +# Flags for the compiler FLAGS = -std=c23 -FLAGS += -O3 -FLAGS += -march=native +FLAGS += -O$(O) +FLAGS += -march=$(ARCH) +# Only override gcc default tuning if specified in make command line +ifneq (, $(TUNE)) + FLAGS += -mtune=$(TUNE) +endif FLAGS += -pedantic FLAGS += -Wall FLAGS += -Wextra FLAGS += -Werror -FLAGS += -Wformat-truncation=0 +# This flag is because of the compiler otherwise giving too much warning +#FLAGS += -Wformat-truncation=0 +# Compile flag for defining GITHASH to put at the end of the version string GITFLAG = -D'GITHASH=$(shell git rev-parse --short=1 HEAD)' +# Directories where .o files and excecutables will be placed OBJDIR = obj OUTDIR = out +# The UI to compile for by default using make run #UI_TARGET = vt100 UI_TARGET = ncurses +# The URL of the plaintext version of the lisence, for the --license option LICENSEURL = https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt +# C compiler CC = cc .NOTINTERMEDIATE: +# Messages to print during different steps in making MSG_FETCHING = FETCHING: MSG_LINKING = Linking: MSG_COMPILING = Compiling C: @@ -29,44 +48,57 @@ MSG_CLEANING = Cleaning: MSG_CLEANING_OBJ = Cleaning $(OBJDIR): MSG_CLEANING_OUT = Cleaning $(OUTDIR): +# Compile for all UI targets all: $(addprefix $(OUTDIR)/connect4_,ncurses.elf vt100.elf) +# Compile and run the default UI target run: $(OUTDIR)/connect4_$(UI_TARGET).elf ./$< +# Compile and run with a specific UI target run_%: $(OUTDIR)/connect4_%.elf ./$< +# Compile specifically the ncurses version +# This one's special because it needs -lncursesw $(OUTDIR)/connect4_ncurses.elf: $(addprefix $(OBJDIR)/,ui_ncurses.o logic.o connect4.o LICENSE.o) @echo $(MSG_LINKING) $@ @mkdir -p $(@D) $(CC) $(FLAGS) -lncursesw -o $@ $^ +# Compile the final excecutable for a given UI target $(OUTDIR)/connect4_%.elf: $(addprefix $(OBJDIR)/,ui_%.o logic.o connect4.o LICENSE.o) @echo $(MSG_LINKING) $@ @mkdir -p $(@D) $(CC) $(FLAGS) -o $@ $^ +# Make an object file for the license so it does not all need to be in the code $(OBJDIR)/LICENSE.o: $(OBJDIR)/LICENSE @echo $(MSG_LINKING) $@ @mkdir -p $(@D) cd $(@D); ld -r -b binary -o $(@F) $(^F) +# Compile the object file of the main code. +# This one's special because it includes the git hash $(OBJDIR)/connect4.o: connect4.c @echo $(MSG_COMPILING) $^ @mkdir -p $(@D) $(CC) -c $(FLAGS) $(GITFLAG) -o $@ $^ +# Generic object file compile $(OBJDIR)/%.o: %.c @echo $(MSG_COMPILING) $^ @mkdir -p $(@D) $(CC) -c $(FLAGS) -o $@ $^ +# Download the license file in non-markdown form. +# This is not cleaned as that would be too silly $(OBJDIR)/LICENSE: @echo $(MSG_FETCHING) $@ curl $(LICENSEURL) \ | sed -n '/END OF TERMS AND CONDITIONS/q;p' > $(OBJDIR)/LICENSE +# Remove all compilation output and intermediate files clean: @echo $(MSG_CLEANING) @echo $(MSG_CLEANING_OBJ) diff --git a/types.h b/types.h index f5b3688..df3c0a4 100644 --- a/types.h +++ b/types.h @@ -5,7 +5,7 @@ #include #include "config.h" -// Only use big ints on architectures where it doesn't impact speed +// Use fastest available ints unless it's 64 bits because that uses more memory #if INT_FAST16_MAX == INT_FAST64_MAX typedef int rowsint_t; typedef unsigned columnsint_t; diff --git a/ui_ncurses.c b/ui_ncurses.c index 00bf47c..8da8914 100644 --- a/ui_ncurses.c +++ b/ui_ncurses.c @@ -71,6 +71,7 @@ void initBoard( const board_t board ){ keypad(stdscr, TRUE); nonl(); echo(); + __attribute__((assume(board.columns < 999))); for( columnsint_t column = 0; column < board.columns; column++ ){ char colnum[4]; snprintf( @@ -96,8 +97,10 @@ void initBoard( const board_t board ){ } for( rowsint_t row = 0; row < board.rows; row++ ){ char rownum[4]; + const int intToPrint = board.rows - row + FIRST_NUMBER - 1; + __attribute__((assume(intToPrint <= 256))); snprintf( - rownum, sizeof(rownum), "%2d", board.rows - row + FIRST_NUMBER - 1 + rownum, sizeof(rownum), "%2d", intToPrint ); mvaddstr( BOARD_Y + BOARD_DY * ( row + 1 ), @@ -211,13 +214,17 @@ columnsint_t askColumn( const board_t board ){ columnsint_t column = 0; -#ifdef ARROWS move( BOARD_Y, BOARD_X ); if( board.player ) addstr( "p1" ); else addstr( "p0" ); refresh(); - for(; board.height[ column ] >= board.rows; ) - column += ( column < board.columns - 1 ); + for(; board.height[ column ] >= board.rows; ){ + column++; + if( column == board.columns ){ + getch(); + return QUITCOLUMN; + } + } for(;;){ int ch = mvgetch( BOARD_Y + BOARD_DY * ( board.rows - board.height[ column ] ), @@ -245,32 +252,6 @@ columnsint_t askColumn( break; } } -#else /* !ARROWS */ - for(;;){ - mvaddstr( - INPUT_Y, - INPUT_X, - "Where does player " - ); - if( board.player ) addstr( "1" ); - else addstr( "0" ); - addstr( " put the piece? " ); - clrtoeol(); - refresh(); - scanw( " %d", &column ); - column -= FIRST_NUMBER; - move( - INPUT_Y + 1, - INPUT_X - ); - clrtoeol(); - if( column >= 0 && column < board.columns ) - if( board.height [ column ] < board.columns ) - return column; - else addstr( "Pls enter a column that ain't full" ); - else addstr( "Pls enter a column that exists" ); - } -#endif /* ! ARROWS */ } void exit_ui(void){ diff --git a/ui_vt100.c b/ui_vt100.c index cbf68b4..03b0a8d 100644 --- a/ui_vt100.c +++ b/ui_vt100.c @@ -65,14 +65,13 @@ void initBoard( const board_t board ){ } void updateBoard( - const wins_t wins, const board_t board, const columnsint_t column ){ rowsint_t height = board.height [ column ]; printf( "\033[%dA\033[%dC%c\033[%dB", - height + 3, + height + 2, column * 3 + 4, '0' + !!( board.player ), height + 1 @@ -85,11 +84,11 @@ void updateBoard( "" WININT_FORMAT " │" WININT_FORMAT "\033[2B\033[8D" "" WININT_FORMAT " │" WININT_FORMAT "\033[2B" ,( 3 * board.columns + 6 ) + SCOREBOARD_WIDTH - 8 - ,wins.count0.vertical, wins.count1.vertical - ,wins.count0.horizontal, wins.count1.horizontal - ,wins.count0.diagonalUp, wins.count1.diagonalUp - ,wins.count0.diagonalDown, wins.count1.diagonalDown - ,wins.count0.total, wins.count1.total + ,board.count0.vertical, board.count1.vertical + ,board.count0.horizontal, board.count1.horizontal + ,board.count0.diagonalUp, board.count1.diagonalUp + ,board.count0.diagonalDown, board.count1.diagonalDown + ,board.count0.total, board.count1.total ); printf( "\033[2K\n" ); } @@ -112,9 +111,10 @@ columnsint_t askColumn( if( column < FIRST_NUMBER ) return QUITCOLUMN; column -= FIRST_NUMBER; if( column < board.columns ) - if( board.height [ column ] < board.rows ) + if( board.height [ column ] < board.rows ){ + printf( "\033[A" ); return column; - else printf( "\033[2Apls enter a column that ain't full" ); + } else printf( "\033[2Apls enter a column that ain't full" ); else printf( "\033[2Apls enter valid column" ); printf( "\033[K\n" );