Compare commits

...

10 commits

9 changed files with 76 additions and 65 deletions

View file

@ -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
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

View file

@ -5,5 +5,3 @@
#define BOARD_HEIGHT 6
#define FIRST_NUMBER 1
#define ARROWS

27
logic.c
View file

@ -17,40 +17,33 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#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++
){

View file

@ -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
);

View file

@ -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

View file

@ -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)

View file

@ -5,7 +5,7 @@
#include <limits.h>
#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;

View file

@ -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){

View file

@ -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" );