Compare commits
10 commits
216afa6e77
...
a5fcaa00d7
| Author | SHA1 | Date | |
|---|---|---|---|
| a5fcaa00d7 | |||
| e31258e26c | |||
| 22356ca529 | |||
| 8c41443935 | |||
| 316fbbc66e | |||
| c72abd8bbf | |||
| 4bfaa26297 | |||
| 315b59ec29 | |||
| e4c5cc3a46 | |||
| b50a6f3486 |
9 changed files with 76 additions and 65 deletions
|
|
@ -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
|
||||
|
|
|
|||
2
config.h
2
config.h
|
|
@ -5,5 +5,3 @@
|
|||
#define BOARD_HEIGHT 6
|
||||
|
||||
#define FIRST_NUMBER 1
|
||||
|
||||
#define ARROWS
|
||||
|
|
|
|||
27
logic.c
27
logic.c
|
|
@ -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++
|
||||
){
|
||||
|
|
|
|||
4
logic.h
4
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
|
||||
);
|
||||
|
|
|
|||
4
macros.h
4
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
|
||||
|
|
|
|||
38
makefile
38
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)
|
||||
|
|
|
|||
2
types.h
2
types.h
|
|
@ -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;
|
||||
|
|
|
|||
41
ui_ncurses.c
41
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){
|
||||
|
|
|
|||
18
ui_vt100.c
18
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" );
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue