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