From 42ae878989c00aa7d77075c9ebcb0bfc60a861c0 Mon Sep 17 00:00:00 2001 From: Abel Stuker Date: Mon, 25 Nov 2024 22:33:42 +0100 Subject: [PATCH] feat: add tic-tac-toes challenge --- tic-tac-toes/.gdb_history | 29 ++++ tic-tac-toes/Dockerfile | 19 +++ tic-tac-toes/Makefile | 49 ++++++ tic-tac-toes/README.md | 15 ++ tic-tac-toes/SOLUTION.md | 36 +++++ tic-tac-toes/docker-compose.yaml | 6 + tic-tac-toes/spawner.py | 47 ++++++ tic-tac-toes/src/board.c | 87 +++++++++++ tic-tac-toes/src/board.h | 26 ++++ tic-tac-toes/src/board.o | Bin 0 -> 3128 bytes tic-tac-toes/src/flag.c | 0 tic-tac-toes/src/flag.o | Bin 0 -> 928 bytes tic-tac-toes/src/player.c | 23 +++ tic-tac-toes/src/player.h | 18 +++ tic-tac-toes/src/player.o | Bin 0 -> 1728 bytes tic-tac-toes/src/tic-tac-toes.c | 246 +++++++++++++++++++++++++++++++ tic-tac-toes/src/tic-tac-toes.o | Bin 0 -> 8448 bytes tic-tac-toes/tic-tac-toes | Bin 0 -> 16768 bytes 18 files changed, 601 insertions(+) create mode 100644 tic-tac-toes/.gdb_history create mode 100644 tic-tac-toes/Dockerfile create mode 100644 tic-tac-toes/Makefile create mode 100644 tic-tac-toes/README.md create mode 100644 tic-tac-toes/SOLUTION.md create mode 100644 tic-tac-toes/docker-compose.yaml create mode 100644 tic-tac-toes/spawner.py create mode 100644 tic-tac-toes/src/board.c create mode 100644 tic-tac-toes/src/board.h create mode 100644 tic-tac-toes/src/board.o create mode 100644 tic-tac-toes/src/flag.c create mode 100644 tic-tac-toes/src/flag.o create mode 100644 tic-tac-toes/src/player.c create mode 100644 tic-tac-toes/src/player.h create mode 100644 tic-tac-toes/src/player.o create mode 100644 tic-tac-toes/src/tic-tac-toes.c create mode 100644 tic-tac-toes/src/tic-tac-toes.o create mode 100755 tic-tac-toes/tic-tac-toes diff --git a/tic-tac-toes/.gdb_history b/tic-tac-toes/.gdb_history new file mode 100644 index 0000000..06a9e70 --- /dev/null +++ b/tic-tac-toes/.gdb_history @@ -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 diff --git a/tic-tac-toes/Dockerfile b/tic-tac-toes/Dockerfile new file mode 100644 index 0000000..3844f3f --- /dev/null +++ b/tic-tac-toes/Dockerfile @@ -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 \ No newline at end of file diff --git a/tic-tac-toes/Makefile b/tic-tac-toes/Makefile new file mode 100644 index 0000000..26be712 --- /dev/null +++ b/tic-tac-toes/Makefile @@ -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) diff --git a/tic-tac-toes/README.md b/tic-tac-toes/README.md new file mode 100644 index 0000000..80ddb01 --- /dev/null +++ b/tic-tac-toes/README.md @@ -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. diff --git a/tic-tac-toes/SOLUTION.md b/tic-tac-toes/SOLUTION.md new file mode 100644 index 0000000..96c9275 --- /dev/null +++ b/tic-tac-toes/SOLUTION.md @@ -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} \ No newline at end of file diff --git a/tic-tac-toes/docker-compose.yaml b/tic-tac-toes/docker-compose.yaml new file mode 100644 index 0000000..e16f08c --- /dev/null +++ b/tic-tac-toes/docker-compose.yaml @@ -0,0 +1,6 @@ +version: '3.9' +services: + tic-tac-toes: + build: . + ports: + - 3005:3005 \ No newline at end of file diff --git a/tic-tac-toes/spawner.py b/tic-tac-toes/spawner.py new file mode 100644 index 0000000..feecba5 --- /dev/null +++ b/tic-tac-toes/spawner.py @@ -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 -p + +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() diff --git a/tic-tac-toes/src/board.c b/tic-tac-toes/src/board.c new file mode 100644 index 0000000..5fdf2d8 --- /dev/null +++ b/tic-tac-toes/src/board.c @@ -0,0 +1,87 @@ +#include +#include +#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; + } +} \ No newline at end of file diff --git a/tic-tac-toes/src/board.h b/tic-tac-toes/src/board.h new file mode 100644 index 0000000..4cb1d74 --- /dev/null +++ b/tic-tac-toes/src/board.h @@ -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 */ \ No newline at end of file diff --git a/tic-tac-toes/src/board.o b/tic-tac-toes/src/board.o new file mode 100644 index 0000000000000000000000000000000000000000..491cdd40c2ec94c4957778ece6a12849a1476321 GIT binary patch literal 3128 zcmbtVPiP!f7=N=(niP{X{y9XN!vw9=){L7_tCb?#hG`Zw3N2fTv}`xClVQz!ycp!sk-!_hzNS{Py`{6Jyg1fJuJrWd-HueFR$xGKX~)K z_xt_6?|tvj%)DX0a5iQbg3S=miD<;A5cR>RpOD>z7!kuFl??t&R-2>O9|iCRMs6dU z4D5EYX18zMWI-}mlSndXNW}(G00)&;kh)V#-3fj&HjS;C-7>Z?IO+Uc>)W^-rs(Q| zxn^(aTr(KjU^$f3usc}F&RFvO{;;M-9le~sYIlt4S|_&tN5rai1#2Ck4Sx_t{(+s5 z`bEq&RDTIwe?1M3Dyue4Slo8QZUH8C3gINL?e6Y2?6!o?)Kab594>!5_>ngtusb!o zL%Z>gefQvX`)=@kRE=6|8)|LG*6R^Vi)aDSimksFieoS*?`yR=7FHFOaDsQ^ky@%< zo9pbY<*m)ifK0LTaCc*Vt=cs0w{<+%@VsbWKpw-z_DkyW&0~|3lj4|IH4i(cSQTP+ zX69M*$n1-$$IYqZ*0eQcPRFN@$4^ehaYMlRpqbF{WI!fj!q`NN&7d*g#fOM6UOy*{ z5841FM$At$eltQ><~VfzYy`45C=Pg59XbSUqu}3 zNqjJWE#f~#9PG4w(EO_r|BYmYII8k)#Q#qG$0{HDKnlhI>kxl&4}ZSPkJBdgS|5ep z<1nO8{Iz=z0l>bVUjW4Ad$cFy0F2=5#D|2+J%IjUXb&iPCWy!LAEmzPo`wPHStr*5 z*jZ@3^v{$26nHdwcHm!zhIz6Gg2^)jpM%ybPw8Lq3gDRM7w~BE?7+VZ4e`Gb1e0e* z9`TQGW5MKEf&UyD;!VPNU1GkN@v@c^`gF<575sG8b&6ib&lL;le6HXUmn-Ca;bijp zBCE4*+4m}rFBUwPRaad{IZs3CDN-J_Y}daMshdi60dG zkfJ|90?dPVk>{OJ@L>f%PdMktn$WH&dbOTcH9gLOwxa0OdbSi?t;dv6_^yPFh9aTi z6Ewb};e5~E)NsD%pJ_P1kDD6K_xZMl^L>V^Dvj&p`)q1B-)CW!SC;(DJb2#={UY}~ zH=nV5ci9(KHsfc6HD4|Z%PWQhr?|9)7t1OXeb<72z+~CaIIqz7YN2A4ykg1q{1u47 zOzvWO!OJYURQdmNCWPaX@D8h%puqrcfMT@#E`skd-nm2<2|IC0_zVT$UBWKD6KG?h z6OVyMobMmOVpf}%aee+LfWVypHGT?WF{eHq&wmg&)%5vK0*P2$6+!<3CB*-hY8=o0 z43OMcb)H@jk-GLA-~-Tf`7!}|UA+H1*EQ&1PCfoh0{Hw`ugBvp>~;Q&SQOZAp_AV> z{0?wDpMS5pG2}KK=qI#b{I>M>h{v4z*#jh&Q4ID~=(=fIajdK-R{iJ+{=C+UR{Pk01^RQz%LROAaY2(pfzm4CctPgIFAlT*EdH|(pRZU zqBKtX@mUftR6U#{9IH4`1R(@-;v@pb&hpY)%GB?{*Y^@h;PJ&wR0g1$Sz*l>v#s0w zB$dtIET{)#4XAO`fXb?($V=qP%H|4>b<$XqJu{w^uWDLX)4aB`&USw#cF{m*U-OLG z!vg(x*vAHODISXnd#V4yBJq=tbmHIPKK|jIx1-jaCJfQHsC(CKZ1`_a+D|sz Ja+c)Z{|B#WGK2sC literal 0 HcmV?d00001 diff --git a/tic-tac-toes/src/player.c b/tic-tac-toes/src/player.c new file mode 100644 index 0000000..981279c --- /dev/null +++ b/tic-tac-toes/src/player.c @@ -0,0 +1,23 @@ +#include +#include + +#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; +} \ No newline at end of file diff --git a/tic-tac-toes/src/player.h b/tic-tac-toes/src/player.h new file mode 100644 index 0000000..b494976 --- /dev/null +++ b/tic-tac-toes/src/player.h @@ -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 */ \ No newline at end of file diff --git a/tic-tac-toes/src/player.o b/tic-tac-toes/src/player.o new file mode 100644 index 0000000000000000000000000000000000000000..75c7db9215c378091b0d72e3dd21444ab286c183 GIT binary patch literal 1728 zcmbtU&1(}u6rW9F8?}{HM3gG*NfhjmgkD;ttc2P{M2ZmcV5ysQYJ>S=cY=u^3WBiY zQg8ilEcT!tz4Z^M5KtlBJP7r@$$RZ~yCUch-n=)z_c1T?X7h6K?(K|a35F#W#Nm!v zAzqCg+O9EOF(oEN*^9n-ufOHJ=*1xF?0Hdn_uWPC4=~1y7Jqtul=8rfY&84jy=XZ{ z8LY=`U6wO)EM^N+}!)lI52~+Sy+cFJ7TF+ns=@( z-78;r3bV2(3r;a#oXyV_^4L-q!~r=00x=>ZtZn2N7VY^C&XdD>eqUH!+nS!7$Zdl2 zGVmw}50_hK>^GUx)Oe-@5hLGCXN^o7_jp10*i&uZ{djr2+fOiXZ!FzOj_a zor1t~G2ZkF+yxLjrN|YE=Qz_la5I01FH(FJG{XEAG}?8)rvh0eW3|?-Y0>cO_0|Ek zra~Qbs#>fEN^xBH%x^>bC9DC!*`WN|AA#=-x$zk9fQ`tIm<=p08O-zVdYs_7e<8zO zL;DrbIvD8QmZ3X?A2gXthn%ES3#F6Z<7($2<| pEjoW2H-OV1IHP3#N#a2r!9zNK;Rt*DuFrx$IeSd8iLC!6|2O2_wm|>@ literal 0 HcmV?d00001 diff --git a/tic-tac-toes/src/tic-tac-toes.c b/tic-tac-toes/src/tic-tac-toes.c new file mode 100644 index 0000000..15fd99c --- /dev/null +++ b/tic-tac-toes/src/tic-tac-toes.c @@ -0,0 +1,246 @@ +#include +#include +#include +#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; +} \ No newline at end of file diff --git a/tic-tac-toes/src/tic-tac-toes.o b/tic-tac-toes/src/tic-tac-toes.o new file mode 100644 index 0000000000000000000000000000000000000000..e4fea0e48767d5b2bfe9eb785a6b7cdf67d516b5 GIT binary patch literal 8448 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 EzqSdjHUIzs literal 0 HcmV?d00001 diff --git a/tic-tac-toes/tic-tac-toes b/tic-tac-toes/tic-tac-toes new file mode 100755 index 0000000000000000000000000000000000000000..a2332db2c3749ceccaad89059592ab00d4e56086 GIT binary patch literal 16768 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 Y^O)>cacM6wx&HermBgia3VEpbFILYh?EnA( literal 0 HcmV?d00001