/* SPDX-License-Identifier: GPL-2.0-only */
/*
* AnnaConnect is a silly little text-based connect 4 game c:
* Copyright (C) 2025 Anna Snoeijs. anna.snoeijs@hotmail.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "ui.h"
#include
#include
#ifndef INPUT_X
#define INPUT_X 2
#endif
#ifndef INPUT_Y
#define INPUT_Y 1
#endif
#ifdef ARROWS
#ifndef SCOREBOARD_X
#define SCOREBOARD_X 2
#endif
#ifndef SCOREBOARD_Y
#define SCOREBOARD_Y 1
#endif
#else
#ifndef SCOREBOARD_X
#define SCOREBOARD_X INPUT_X
#endif
#ifndef SCOREBOARD_Y
#define SCOREBOARD_Y INPUT_Y + 2
#endif
#endif
#ifndef BOARD_X
#define BOARD_X SCOREBOARD_X + 28
#endif
#ifndef BOARD_Y
#define BOARD_Y SCOREBOARD_Y + 1
#endif
#ifndef BOARD_DX
#define BOARD_DX 3
#endif
#ifndef BOARD_DY
#define BOARD_DY 1
#endif
#ifndef SCOREBOARD_HEIGHT
#define SCOREBOARD_HEIGHT 10
#endif
void initBoard( const board_t board ){
setlocale(LC_ALL, "");
initscr();
keypad(stdscr, TRUE);
nonl();
echo();
for( int column = 0; column < board.columns; column++ ){
char colnum[4];
sprintf( colnum, "%2d", column + FIRST_NUMBER );
mvaddstr(
BOARD_Y,
BOARD_X + BOARD_DX * ( column + 1 ),
colnum
);
for(int row = 0; row < board.rows; row++){
mvaddstr(
BOARD_Y + BOARD_DY * ( row + 1 ),
BOARD_X + BOARD_DX * ( column + 1 ),
"[ ]"
);
}
mvaddstr(
BOARD_Y + BOARD_DY * ( board.rows + 1 ),
BOARD_X + BOARD_DX * ( column + 1 ),
colnum
);
}
for(int row = 0; row < board.rows; row++ ){
char rownum[4];
sprintf( rownum, "%2d", board.rows - row + FIRST_NUMBER - 1 );
mvaddstr(
BOARD_Y + BOARD_DY * ( row + 1 ),
BOARD_X,
rownum
);
mvaddstr(
BOARD_Y + BOARD_DY * ( row + 1 ),
BOARD_X + BOARD_DX * ( board.columns + 1 ),
rownum
);
}
for( int y = 0; y < SCOREBOARD_HEIGHT; y++ ){
char *str;
switch(y){
case 0: str = "┌───────────────┬────┬────┐"; break;
case 1: str = "│ player │ 0 │ 1 │"; break;
case 2: str = "├───────────────┼────┼────┤"; break;
case 3: str = "│ vertical │ │ │"; break;
case 4: str = "│ horizontal │ │ │"; break;
case 5: str = "│ diagonal up │ │ │"; break;
case 6: str = "│ diagonal down │ │ │"; break;
case 7: str = "├───────────────┼────┼────┤"; break;
case 8: str = "│ total │ │ │"; break;
case 9: str = "└───────────────┴────┴────┘"; break;
default: str = "The code be brokeys at initboard for the score";
}
mvaddstr(
SCOREBOARD_Y + y,
SCOREBOARD_X,
str
);
}
}
void updateBoard(
const wins_t wins,
const board_t board,
const move_t move,
const int column
){
int height = board.height[ column ];
switch( move ){
case PUT:
mvaddstr(
BOARD_Y + BOARD_DY * ( board.rows - height + 1 ),
BOARD_X + 1 + BOARD_DX * ( column + 1 ),
board.column[ column ] & 1 << ( height - 1 ) ? "1" : "0"
);
break;
default:
for( int row = 0; row < height; row++ ){
mvaddstr(
BOARD_Y + BOARD_DY * ( board.rows - row ),
BOARD_X + 1 + BOARD_DX * ( column + 1 ),
board.column[ column ] & 1 << row ? "1" : "0"
);
}
break;
}
char num[4];
sprintf( num, "%3d", wins.count0.vertical );
mvaddstr(
SCOREBOARD_Y + 3,
SCOREBOARD_X + 17,
num
);
sprintf( num, "%3d", wins.count1.vertical );
mvaddstr(
SCOREBOARD_Y + 3,
SCOREBOARD_X + 22,
num
);
sprintf( num, "%3d", wins.count0.horizontal );
mvaddstr(
SCOREBOARD_Y + 4,
SCOREBOARD_X + 17,
num
);
sprintf( num, "%3d", wins.count1.horizontal );
mvaddstr(
SCOREBOARD_Y + 4,
SCOREBOARD_X + 22,
num
);
sprintf( num, "%3d", wins.count0.diagonalUp );
mvaddstr(
SCOREBOARD_Y + 5,
SCOREBOARD_X + 17,
num
);
sprintf( num, "%3d", wins.count1.diagonalUp );
mvaddstr(
SCOREBOARD_Y + 5,
SCOREBOARD_X + 22,
num
);
sprintf( num, "%3d", wins.count0.diagonalDown );
mvaddstr(
SCOREBOARD_Y + 6,
SCOREBOARD_X + 17,
num
);
sprintf( num, "%3d", wins.count1.diagonalDown );
mvaddstr(
SCOREBOARD_Y + 6,
SCOREBOARD_X + 22,
num
);
sprintf( num, "%3d", wins.count0.total );
mvaddstr(
SCOREBOARD_Y + 8,
SCOREBOARD_X + 17,
num
);
sprintf( num, "%3d", wins.count1.total );
mvaddstr(
SCOREBOARD_Y + 8,
SCOREBOARD_X + 22,
num
);
}
int askColumn(
const board_t board,
const move_t move
){
int 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(;;){
int ch = mvgetch(
BOARD_Y + BOARD_DY * ( board.rows - board.height[ column ] ),
BOARD_X + 1 + BOARD_DX * ( column + 1 )
);
switch( ch ){
case KEY_RIGHT:
column += ( column < board.columns - 1 );
break;
case KEY_LEFT:
column -= ( column > 0 );
break;
case KEY_DOWN:
return column;
}
switch( move ){
case PUT:
for(; board.height[ column ] >= board.rows; ) switch( ch ){
case KEY_RIGHT:
if( column >= board.columns ) ch = KEY_LEFT;
else column++;
break;
case KEY_LEFT:
if( column == 0 ) ch = KEY_RIGHT;
else column--;
break;
}
break;
case POP:
for(; board.height[ column ] <= 0; ) switch( ch ){
case KEY_RIGHT:
if( column >= board.columns ) ch = KEY_LEFT;
else column++;
break;
case KEY_LEFT:
if( column == 0 ) ch = KEY_RIGHT;
else column--;
break;
}
break;
default:
// Do nothing
}
}
#else /* !ARROWS */
for(;;){
mvaddstr(
INPUT_Y,
INPUT_X,
"Where does player "
);
if( board.player ) addstr( "1" );
else addstr( "0" );
switch( move ){
case PUT:
addstr( " put the piece? " );
case POP:
addstr( " pop a piece? " );
default:
addstr( " Undefined move a thingy? " );
}
clrtoeol();
refresh();
scanw( " %d", &column );
column -= FIRST_NUMBER;
move(
INPUT_Y + 1,
INPUT_X
);
clrtoeol();
if( column >= 0 && column < board.columns )
switch( move ){
case PUT:
if( board.height [ column ] < board.columns )
return column;
else addstr( "Pls enter a column that ain't full" );
break;
case POP:
if( board.height [ column ] != 0 )
return column;
else addstr( "Pls enter a column that ain't empty" );
default:
return column;
}
else addstr( "Pls enter a column that exists" );
}
#endif /* ! ARROWS */
}
move_t askMove( void ){
move_t move = PUT;
// TODO: actually ask what move
return move;
}
#undef INPUT_X
#undef INPUT_Y
#undef SCOREBOARD_X
#undef SCOREBOARD_Y
#undef BOARD_X
#undef BOARD_Y
#undef BOARD_DX
#undef BOARD_DY
#undef SCOREBOARD_HEIGHT