feat: add tic-tac-toes challenge
This commit is contained in:
parent
279be322d7
commit
42ae878989
29
tic-tac-toes/.gdb_history
Normal file
29
tic-tac-toes/.gdb_history
Normal file
@ -0,0 +1,29 @@
|
||||
disassemble main
|
||||
break print_win_message
|
||||
d 1
|
||||
break main
|
||||
r
|
||||
disassemble print_win_message
|
||||
break *0x00005555555554c2
|
||||
c
|
||||
r
|
||||
c
|
||||
c
|
||||
r
|
||||
c
|
||||
c
|
||||
r
|
||||
c
|
||||
c
|
||||
r
|
||||
c
|
||||
r
|
||||
c
|
||||
c
|
||||
r
|
||||
c
|
||||
c
|
||||
r
|
||||
c
|
||||
c
|
||||
disassemble print_win_message
|
19
tic-tac-toes/Dockerfile
Normal file
19
tic-tac-toes/Dockerfile
Normal file
@ -0,0 +1,19 @@
|
||||
FROM debian:bullseye
|
||||
|
||||
RUN apt update -y && apt install -y python3 build-essential
|
||||
|
||||
RUN useradd -m ctf
|
||||
|
||||
WORKDIR /app
|
||||
COPY src /app/src
|
||||
COPY Makefile /app/Makefile
|
||||
COPY spawner.py /app/spawner.py
|
||||
|
||||
ENV FLAG="\"\\\"IGCTF{W3ll_y0u_st1ll_l0st_BuT_at_l3ast_yoU_g0t_th3_fl4g}\\\"\""
|
||||
|
||||
RUN make clean
|
||||
RUN make
|
||||
|
||||
CMD ["python3", "spawner.py", "--host", "0.0.0.0", "--port", "3005", "./tic-tac-toes"]
|
||||
|
||||
EXPOSE 3005
|
49
tic-tac-toes/Makefile
Normal file
49
tic-tac-toes/Makefile
Normal file
@ -0,0 +1,49 @@
|
||||
CC=gcc
|
||||
LD=gcc
|
||||
|
||||
# I'm using GCC to link code. This is prefered over using LD directly since
|
||||
# it will handle all the GLIBC linkage for you.
|
||||
|
||||
SRCS = $(wildcard src/*.c)
|
||||
OBJS = ${SRCS:.c=.o}
|
||||
|
||||
DEST = tic-tac-toes
|
||||
|
||||
CC_FLAGS = -Wall -Wextra -Werror -Wno-unused-parameter -Wno-unused-variable -Wno-unused-function
|
||||
LD_FLAGS =
|
||||
LIBS =
|
||||
|
||||
ifeq ($(FLAG),)
|
||||
FLAG := "\"IGCTF{REDACTED}\""
|
||||
endif
|
||||
|
||||
# This is to let make know these are not actually file targets but rather
|
||||
# command targets.
|
||||
.PHONY = all clean format debug run
|
||||
|
||||
all: $(DEST)
|
||||
|
||||
# Link all objects together into one binary
|
||||
$(DEST): ${OBJS}
|
||||
$(LD) ${LD_FLAGS} $^ $(LIBS) -o $(DEST)
|
||||
|
||||
# For every c file, compile it to an object file
|
||||
%.o: %.c
|
||||
$(CC) ${CC_FLAGS} -c $< -o $@ -DFLAG=$(FLAG)
|
||||
|
||||
# Run the formatter
|
||||
format:
|
||||
clang-format -i src/*.c src/*.h
|
||||
|
||||
# Run the created binary
|
||||
run: $(DEST)
|
||||
./$(DEST)
|
||||
|
||||
# Run the created binary using the debugger
|
||||
debug: $(DEST)
|
||||
gdb ./$(DEST) -ex "set disassembly-flavor intel"
|
||||
|
||||
# Clean the project of all build files
|
||||
clean:
|
||||
$(info [INFO] Cleaning directory)
|
||||
rm -rf ${OBJS} $(DEST)
|
15
tic-tac-toes/README.md
Normal file
15
tic-tac-toes/README.md
Normal file
@ -0,0 +1,15 @@
|
||||
# TicTacToes
|
||||
## Text
|
||||
Everyone has that one weird friend, mine just sent me a game they made.
|
||||
They bet me that I can't beat their game and if I can't, I have to send
|
||||
them a pic of myself in sandals??? Anyway I really don't want to, so please
|
||||
just win this for me.
|
||||
|
||||
## Author
|
||||
Robbe De Greef
|
||||
|
||||
## Files
|
||||
tic-tac-toes (the binary)
|
||||
|
||||
## How to Deploy
|
||||
Deploy docker compose.
|
36
tic-tac-toes/SOLUTION.md
Normal file
36
tic-tac-toes/SOLUTION.md
Normal file
@ -0,0 +1,36 @@
|
||||
## Difficulty
|
||||
50/100 | MEDIUM
|
||||
|
||||
Simple format string attack, this is not groundbreaking stuff.
|
||||
|
||||
## Category
|
||||
Exploitation
|
||||
## How To Solve
|
||||
There is a format string vulnerability in the `print_win_message` function.
|
||||
The name the user inputs at the beginning of the game is printed here using
|
||||
`printf` without any validation. This means we can leak data from the stack.
|
||||
It also happens that the flag is on the stack.
|
||||
To leak the flag we are going to use the `%s` format string specifier. The
|
||||
problem is that there are a couple of values on the stack before that, for
|
||||
example the stack looks like this right before the `printf` call:
|
||||
|
||||
```
|
||||
00:0000│ rsp 0x7fffffffd250 —▸ 0x5555555596b0 ◂— '%x%x%x%s'
|
||||
01:0008│-018 0x7fffffffd258 —▸ 0x55555555670b ◂— 'Xx_TicTacToesKing69_xX'
|
||||
02:0010│-010 0x7fffffffd260 —▸ 0x555555559720 —▸ 0x55555555670b ◂— 'Xx_TicTacToesKing69_xX'
|
||||
03:0018│-008 0x7fffffffd268 —▸ 0x55555555667f ◂— 'IGCTF{REDACTED}'
|
||||
04:0020│ rbp 0x7fffffffd270 —▸ 0x7fffffffd2b0 —▸ 0x7fffffffd2e0 —▸ 0x7fffffffd380 —▸ 0x7fffffffd3e0 ◂— ...
|
||||
05:0028│+008 0x7fffffffd278 —▸ 0x555555555b64 (play_game+240) ◂— mov rax, qword ptr [rbp - 0x18]
|
||||
06:0030│+010 0x7fffffffd280 —▸ 0x555555559740 —▸ 0x5555555596b0 ◂— '%x%x%x%s'
|
||||
07:0038│+018 0x7fffffffd288 —▸ 0x555555559720 —▸ 0x55555555670b ◂— 'Xx_TicTacToesKing69_xX'
|
||||
```
|
||||
|
||||
So we need to pop off, 3 64 bit ints before we reach the flag. Which means we would
|
||||
need the following payload `%p%p%p%s`. This would work on a 32 bit machine but since
|
||||
this is a 64 bit binary, the first 6 arguments are passed via register. This means
|
||||
we need to "pop off" an additional 5 arguments (the first argument is the format
|
||||
string itself) giving us the final payload string:
|
||||
`%p%p%p%p%p%p%p%p%s`
|
||||
|
||||
## Flag
|
||||
IGCTF{W3ll_y0u_st1ll_l0st_BuT_at_l3ast_yoU_g0t_th3_fl4g}
|
6
tic-tac-toes/docker-compose.yaml
Normal file
6
tic-tac-toes/docker-compose.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
version: '3.9'
|
||||
services:
|
||||
tic-tac-toes:
|
||||
build: .
|
||||
ports:
|
||||
- 3005:3005
|
47
tic-tac-toes/spawner.py
Normal file
47
tic-tac-toes/spawner.py
Normal file
@ -0,0 +1,47 @@
|
||||
import socket
|
||||
from threading import Thread
|
||||
import subprocess
|
||||
import sys
|
||||
import argparse
|
||||
|
||||
# How to use:
|
||||
# Set the host and port above
|
||||
# Invoke the spawner like this
|
||||
# python3 spawner.py -h <host> -p <port> <your arguments to spawn here>
|
||||
|
||||
def on_new_client(conn, addr, cmd):
|
||||
print(f"New connection with {addr}")
|
||||
file = conn.makefile('w')
|
||||
p = subprocess.Popen(cmd, stdout=file, stdin=file)
|
||||
conn.close()
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Spawn some processes over a socket")
|
||||
parser.add_argument('--host', type=str, required=True, nargs=1, help='The host ip to listen on')
|
||||
parser.add_argument('--port', type=int, required=True, nargs=1, help='The host port to listen on')
|
||||
parser.add_argument('rest', nargs=argparse.REMAINDER)
|
||||
args = parser.parse_args()
|
||||
|
||||
s = socket.socket()
|
||||
host = args.host[0]
|
||||
port = args.port[0]
|
||||
cmd = args.rest
|
||||
print(f"Command spawn each time: {cmd}")
|
||||
|
||||
s.bind((host, port))
|
||||
s.listen(5)
|
||||
|
||||
print(f"Now listening for connections on {host}:{port}")
|
||||
try:
|
||||
while True:
|
||||
conn, addr = s.accept()
|
||||
t = Thread(target=on_new_client, args=(conn,addr, cmd))
|
||||
t.run()
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
|
||||
s.close()
|
||||
print("Goodbye")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
87
tic-tac-toes/src/board.c
Normal file
87
tic-tac-toes/src/board.c
Normal file
@ -0,0 +1,87 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "board.h"
|
||||
|
||||
struct board *board_init()
|
||||
{
|
||||
struct board *board = calloc(1, sizeof(struct board));
|
||||
return board;
|
||||
}
|
||||
|
||||
void board_destruct(struct board *board)
|
||||
{
|
||||
free(board);
|
||||
}
|
||||
|
||||
enum cell_state board_get_cell(struct board *board, int x, int y)
|
||||
{
|
||||
return board->cells[y * WIDTH + x];
|
||||
}
|
||||
|
||||
void board_set_cell(struct board *board, int x, int y, enum cell_state state)
|
||||
{
|
||||
board->cells[y * WIDTH + x] = state;
|
||||
}
|
||||
|
||||
static void board_print_decoration_line()
|
||||
{
|
||||
for (int x = 0; x < WIDTH; x++)
|
||||
{
|
||||
printf("+---");
|
||||
}
|
||||
printf("+\n");
|
||||
}
|
||||
|
||||
char board_cell_char_repr(enum cell_state cell)
|
||||
{
|
||||
switch (cell)
|
||||
{
|
||||
case CROSS:
|
||||
return 'X';
|
||||
case NOUGHT:
|
||||
return 'O';
|
||||
default:
|
||||
return '_';
|
||||
}
|
||||
}
|
||||
|
||||
void board_print(struct board *board)
|
||||
{
|
||||
for (int y = 0; y < HEIGHT; y++)
|
||||
{
|
||||
board_print_decoration_line();
|
||||
for (int x = 0; x < WIDTH; x++)
|
||||
{
|
||||
enum cell_state cell = board_get_cell(board, x, y);
|
||||
char c = board_cell_char_repr(cell);
|
||||
printf("| %c ", c);
|
||||
}
|
||||
printf("|\n");
|
||||
}
|
||||
board_print_decoration_line();
|
||||
}
|
||||
|
||||
int board_is_full(struct board *board)
|
||||
{
|
||||
for (int y = 0; y < HEIGHT; y++)
|
||||
{
|
||||
for (int x = 0; x < WIDTH; x++)
|
||||
{
|
||||
if (board_get_cell(board, x, y) == NOPLAYER)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
enum cell_state get_opponent(enum cell_state player)
|
||||
{
|
||||
if (player == CROSS)
|
||||
{
|
||||
return NOUGHT;
|
||||
}
|
||||
else
|
||||
{
|
||||
return CROSS;
|
||||
}
|
||||
}
|
26
tic-tac-toes/src/board.h
Normal file
26
tic-tac-toes/src/board.h
Normal file
@ -0,0 +1,26 @@
|
||||
#ifndef BOARD_H
|
||||
#define BOARD_H
|
||||
|
||||
#define WIDTH 3
|
||||
#define HEIGHT 3
|
||||
|
||||
enum cell_state {
|
||||
NOPLAYER = 0,
|
||||
CROSS,
|
||||
NOUGHT,
|
||||
};
|
||||
|
||||
struct board {
|
||||
enum cell_state cells[WIDTH * HEIGHT];
|
||||
};
|
||||
|
||||
struct board* board_init();
|
||||
void board_destruct(struct board* board);
|
||||
enum cell_state board_get_cell(struct board* board, int x, int y);
|
||||
void board_set_cell(struct board* board, int x, int y, enum cell_state state);
|
||||
void board_print(struct board* board);
|
||||
int board_is_full(struct board* board);
|
||||
enum cell_state get_opponent(enum cell_state player);
|
||||
char board_cell_char_repr(enum cell_state cell);
|
||||
|
||||
#endif /* BOARD_H */
|
BIN
tic-tac-toes/src/board.o
Normal file
BIN
tic-tac-toes/src/board.o
Normal file
Binary file not shown.
0
tic-tac-toes/src/flag.c
Normal file
0
tic-tac-toes/src/flag.c
Normal file
BIN
tic-tac-toes/src/flag.o
Normal file
BIN
tic-tac-toes/src/flag.o
Normal file
Binary file not shown.
23
tic-tac-toes/src/player.c
Normal file
23
tic-tac-toes/src/player.c
Normal file
@ -0,0 +1,23 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "player.h"
|
||||
|
||||
struct player* player_init(char* name, enum cell_state color, player_move_fptr move) {
|
||||
struct player* player = malloc(sizeof(struct player));
|
||||
player->name = name;
|
||||
player->color = color;
|
||||
player->move = move;
|
||||
return player;
|
||||
}
|
||||
|
||||
void player_destruct(struct player* player)
|
||||
{
|
||||
free(player);
|
||||
}
|
||||
|
||||
struct player* player_swap(struct player* cur, struct player* first, struct player* second)
|
||||
{
|
||||
if (cur->color == first->color) return second;
|
||||
return first;
|
||||
}
|
18
tic-tac-toes/src/player.h
Normal file
18
tic-tac-toes/src/player.h
Normal file
@ -0,0 +1,18 @@
|
||||
#ifndef PLAYER_H
|
||||
#define PLAYER_H
|
||||
|
||||
#include "board.h"
|
||||
|
||||
typedef void (*player_move_fptr)(struct board*, enum cell_state);
|
||||
|
||||
struct player {
|
||||
char* name;
|
||||
enum cell_state color;
|
||||
player_move_fptr move;
|
||||
};
|
||||
|
||||
struct player* player_init(char* name, enum cell_state color, player_move_fptr move);
|
||||
void player_destruct(struct player* player);
|
||||
struct player* player_swap(struct player* cur, struct player* first, struct player* second);
|
||||
|
||||
#endif /* PLAYER_H */
|
BIN
tic-tac-toes/src/player.o
Normal file
BIN
tic-tac-toes/src/player.o
Normal file
Binary file not shown.
246
tic-tac-toes/src/tic-tac-toes.c
Normal file
246
tic-tac-toes/src/tic-tac-toes.c
Normal file
@ -0,0 +1,246 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <math.h>
|
||||
#include "board.h"
|
||||
#include "player.h"
|
||||
|
||||
#ifndef FLAG
|
||||
#define FLAG "IGCTF{REDACTED}"
|
||||
#endif
|
||||
|
||||
#define max(x, y) (((x) > (y)) ? (x) : (y))
|
||||
#define min(x, y) (((x) < (y)) ? (x) : (y))
|
||||
|
||||
const char *toes =
|
||||
" @@@@@@@@@@ @@@#@@@@*@@ \n"
|
||||
" @@%:::::::::-%@@ @#::#@#+*%@#@@ \n"
|
||||
" @%::::--==--::::%@ @@+::=@:....:@+@@ @@@@@ \n"
|
||||
" @*:*@%+-....:+%@*:*@ @%::::#@-..:%#:%@%*%%##@@@ \n"
|
||||
" @#:*#............%=:*@ @%::::::=##+:::@++@...-%%@@ \n"
|
||||
" @@-:+#............@-::%@@@=:::::::::::=@-+@:...+@*@ \n"
|
||||
" @%::-@...........=@:::-@ @@-::::::::::#*:::*%%%*:*@ \n"
|
||||
" @+:::##.........-@-::::%@ @@-:::::::::@=:::::::::%@ @@@@@@ \n"
|
||||
" @=::::+@*-...:+@*::::::*@ @=:::::::::%+:::::::::@@@=::+#%@@ \n"
|
||||
" @=:::::::=***+:::::::::*@ @*:::::::::*#:::::::::@+::-@-..:%@@ \n"
|
||||
" @*:::::::::::::::::::::#@ @*:::::::::*%:::::::::%+:::*%:.:@#@ \n"
|
||||
" @@:::::::::::::::::::::@@ @*:::::::::*#:::::::::%*:::::+*+::@ \n"
|
||||
" @@-:::::::::::::::::::#@ @+:::::::::#+:::::::::%#::::::::::@ \n"
|
||||
" @+::::::::::::::::::-@@ @-:::::::::%=:::::::::%*::::::::::@ \n"
|
||||
" @@::::::::::::::::::*@ @@-:::::::::%=:::::::::@*::::::::::@@@@@@ \n"
|
||||
" @%:::::::::::::::::@@ @%::::::::::#*::::::::+@@:::::::::-@::-#@@@ \n"
|
||||
" @@-::: _____ _ :@ _____ :::: _____ ::::+@:::::::::: :-@..=@ \n"
|
||||
" @+::: |_ _(_)__ |_ _|_ _ __ |_ _|__ ___ ___ :::::::*@@@@@ \n"
|
||||
" @+::: | | | / _| | |/ _` / _| | |/ _ \\/ -_|_-< :::::::::::*@ \n"
|
||||
" @+::: |_| |_\\__| |_|\\__,_\\__| |_|\\___/\\___/__/ ::::::::::*@ \n"
|
||||
" @+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*@ \n";
|
||||
|
||||
void print_banner()
|
||||
{
|
||||
puts(toes);
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
void print_win_message(char *winner, char *loser)
|
||||
{
|
||||
char* flag = FLAG;
|
||||
puts("And the flag goes tooo...");
|
||||
puts(winner);
|
||||
puts("Guess who lost...");
|
||||
printf(loser);
|
||||
}
|
||||
|
||||
int has_player_won(struct board *board, enum cell_state player)
|
||||
{
|
||||
// Rows
|
||||
for (int y = 0; y < HEIGHT; y++)
|
||||
{
|
||||
enum cell_state x0 = board_get_cell(board, 0, y);
|
||||
enum cell_state x1 = board_get_cell(board, 1, y);
|
||||
enum cell_state x2 = board_get_cell(board, 2, y);
|
||||
|
||||
if (x0 == x1 && x1 == x2 && x0 == player)
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Cols
|
||||
for (int x = 0; x < WIDTH; x++)
|
||||
{
|
||||
enum cell_state y0 = board_get_cell(board, x, 0);
|
||||
enum cell_state y1 = board_get_cell(board, x, 1);
|
||||
enum cell_state y2 = board_get_cell(board, x, 2);
|
||||
|
||||
if (y0 == y1 && y1 == y2 && y0 == player)
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Diags
|
||||
enum cell_state p0 = board_get_cell(board, 0, 0);
|
||||
enum cell_state p1 = board_get_cell(board, 1, 1);
|
||||
enum cell_state p2 = board_get_cell(board, 2, 2);
|
||||
|
||||
if (p0 == p1 && p1 == p2 && p0 == player)
|
||||
return 1;
|
||||
|
||||
p0 = board_get_cell(board, 2, 0);
|
||||
p1 = board_get_cell(board, 1, 1);
|
||||
p2 = board_get_cell(board, 0, 2);
|
||||
|
||||
if (p0 == p1 && p1 == p2 && p0 == player)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int minimax(struct board *board, int depth, int isMaximizingPlayer, enum cell_state player)
|
||||
{
|
||||
if (has_player_won(board, CROSS))
|
||||
{
|
||||
return 10;
|
||||
}
|
||||
else if (has_player_won(board, NOUGHT))
|
||||
{
|
||||
return -10;
|
||||
}
|
||||
else if (board_is_full(board))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (isMaximizingPlayer)
|
||||
{
|
||||
int bestScore = -1000;
|
||||
|
||||
for (int x = 0; x < WIDTH; x++)
|
||||
{
|
||||
for (int y = 0; y < HEIGHT; y++)
|
||||
{
|
||||
if (board_get_cell(board, x, y) == NOPLAYER)
|
||||
{
|
||||
board_set_cell(board, x, y, player);
|
||||
int score = minimax(board, depth + 1, 0, player);
|
||||
board_set_cell(board, x, y, NOPLAYER);
|
||||
bestScore = max(score, bestScore);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bestScore;
|
||||
}
|
||||
else
|
||||
{
|
||||
int best_score = 1000;
|
||||
|
||||
for (int x = 0; x < WIDTH; x++)
|
||||
{
|
||||
for (int y = 0; y < HEIGHT; y++)
|
||||
{
|
||||
if (board_get_cell(board, x, y) == NOPLAYER)
|
||||
{
|
||||
board_set_cell(board, x, y, get_opponent(player));
|
||||
int score = minimax(board, depth + 1, 1, player);
|
||||
board_set_cell(board, x, y, NOPLAYER);
|
||||
best_score = min(score, best_score);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return best_score;
|
||||
}
|
||||
}
|
||||
|
||||
void ai_do_move(struct board *board, enum cell_state player)
|
||||
{
|
||||
int best_score = -1000;
|
||||
int best_move_x = -1;
|
||||
int best_move_y = -1;
|
||||
|
||||
for (int x = 0; x < WIDTH; x++)
|
||||
{
|
||||
for (int y = 0; y < HEIGHT; y++)
|
||||
{
|
||||
if (board_get_cell(board, x, y) == NOPLAYER)
|
||||
{
|
||||
board_set_cell(board, x, y, player);
|
||||
int score = minimax(board, 0, 0, player);
|
||||
board_set_cell(board, x, y, NOPLAYER);
|
||||
|
||||
if (score > best_score)
|
||||
{
|
||||
best_score = score;
|
||||
best_move_x = x;
|
||||
best_move_y = y;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
board_set_cell(board, best_move_x, best_move_y, player);
|
||||
}
|
||||
|
||||
void user_do_move(struct board *board, enum cell_state player)
|
||||
{
|
||||
int x, y;
|
||||
|
||||
while (1)
|
||||
{
|
||||
printf("x: ");
|
||||
scanf("%i", &x);
|
||||
printf("y: ");
|
||||
scanf("%i", &y);
|
||||
|
||||
x -= 1;
|
||||
y -= 1;
|
||||
|
||||
if (x < 0 || x > 2 || y < 0 || y > 2)
|
||||
continue;
|
||||
|
||||
if (board_get_cell(board, x, y) != NOPLAYER)
|
||||
continue;
|
||||
|
||||
board_set_cell(board, x, y, player);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void play_game(struct player *player1, struct player *player2)
|
||||
{
|
||||
struct board *board = board_init();
|
||||
struct player *player = player2;
|
||||
|
||||
while (!has_player_won(board, player->color))
|
||||
{
|
||||
player = player_swap(player, player1, player2);
|
||||
|
||||
printf("%s can now move\n", player->name);
|
||||
board_print(board);
|
||||
|
||||
player->move(board, player->color);
|
||||
}
|
||||
board_print(board);
|
||||
|
||||
// Laigh
|
||||
char *winner = player->name;
|
||||
player = player_swap(player, player1, player2);
|
||||
char *loser = player->name;
|
||||
print_win_message(winner, loser);
|
||||
|
||||
board_destruct(board);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
setbuf(stdout, NULL);
|
||||
|
||||
char *username = NULL;
|
||||
|
||||
print_banner();
|
||||
|
||||
printf("Before we start playing, what is your name?\n> ");
|
||||
scanf("%ms", &username);
|
||||
|
||||
struct player *player1 = player_init("Xx_TicTacToesKing69_xX", CROSS, ai_do_move);
|
||||
struct player *player2 = player_init(username, NOUGHT, user_do_move);
|
||||
|
||||
play_game(player1, player2);
|
||||
return 0;
|
||||
}
|
BIN
tic-tac-toes/src/tic-tac-toes.o
Normal file
BIN
tic-tac-toes/src/tic-tac-toes.o
Normal file
Binary file not shown.
BIN
tic-tac-toes/tic-tac-toes
Executable file
BIN
tic-tac-toes/tic-tac-toes
Executable file
Binary file not shown.
Loading…
Reference in New Issue
Block a user