v1.0.0
This commit is contained in:
commit
e3c4e9c100
|
@ -0,0 +1,3 @@
|
||||||
|
.vscode
|
||||||
|
build
|
||||||
|
tictactoe
|
|
@ -0,0 +1,50 @@
|
||||||
|
# TicTacToe
|
||||||
|
|
||||||
|
Крестики-нолики
|
||||||
|
|
||||||
|
![game](images/game.png)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
~ $ ./tictactoe --help
|
||||||
|
Использование: tictactoe [option] [arguments] ...
|
||||||
|
|
||||||
|
-h, --help Получить информацию об использовании
|
||||||
|
-s, --size <count columns> Размер сетки N*N
|
||||||
|
-w, --width <size> Ширина/высота игрового окна
|
||||||
|
-m, --margin <size> Размер внутреннего отступа от границы окна до игрового поля
|
||||||
|
-v, --version Версия TicTacToe
|
||||||
|
```
|
||||||
|
|
||||||
|
## Сборка
|
||||||
|
|
||||||
|
```sh
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake -B . -S ../game
|
||||||
|
make
|
||||||
|
```
|
||||||
|
|
||||||
|
Основные данные, необходимые для запуска игры, находятся в каталоге [data](data/).
|
||||||
|
|
||||||
|
### Для Windows
|
||||||
|
|
||||||
|
#### Ключи для сборки
|
||||||
|
|
||||||
|
`-lallegro_dialog-static -lallegro_image-static -lallegro_primitives-static -lallegro-static -ljpeg -lpng16 -lwebp -lwinmm -lopengl32 -lcomdlg32 -lgdi32 -lole32 -lshlwapi -lz -mwindows`
|
||||||
|
|
||||||
|
#### Статическая сборка
|
||||||
|
|
||||||
|
`-static`
|
||||||
|
|
||||||
|
#### Дополнительные библиотеки для сборки
|
||||||
|
|
||||||
|
- `jpeg`
|
||||||
|
- `png16`
|
||||||
|
- `webp`
|
||||||
|
- `winmm`
|
||||||
|
- `opengl32`
|
||||||
|
- `comdlg32`
|
||||||
|
- `gdi32`
|
||||||
|
- `ole32`
|
||||||
|
- `shlwapi`
|
||||||
|
- `z`
|
Binary file not shown.
After Width: | Height: | Size: 37 KiB |
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
|
@ -0,0 +1,24 @@
|
||||||
|
cmake_minimum_required(VERSION 3.0)
|
||||||
|
project(tictactoe)
|
||||||
|
|
||||||
|
include_directories("lib/" "network/" "objects/")
|
||||||
|
|
||||||
|
set(SRC_GAME
|
||||||
|
ai.cpp
|
||||||
|
main.cpp
|
||||||
|
map.cpp
|
||||||
|
parse_args.cpp
|
||||||
|
version.cpp)
|
||||||
|
|
||||||
|
find_library(ALLEGRO_LIB NAMES allegro)
|
||||||
|
find_library(ALLEGRO_PRIMITIVES_LIB NAMES allegro_primitives)
|
||||||
|
find_library(ALLEGRO_DIALOG_LIB NAMES allegro_dialog)
|
||||||
|
find_library(ALLEGRO_IMAGE_LIB allegro_image)
|
||||||
|
|
||||||
|
add_executable(${PROJECT_NAME} ${SRC_GAME})
|
||||||
|
|
||||||
|
target_link_libraries(${PROJECT_NAME}
|
||||||
|
${ALLEGRO_LIB}
|
||||||
|
${ALLEGRO_PRIMITIVES_LIB}
|
||||||
|
${ALLEGRO_DIALOG_LIB}
|
||||||
|
${ALLEGRO_IMAGE_LIB})
|
|
@ -0,0 +1,118 @@
|
||||||
|
#include "ai.hpp"
|
||||||
|
|
||||||
|
std::random_device rd;
|
||||||
|
std::mt19937 mt(rd());
|
||||||
|
|
||||||
|
void aiTurn(map *m)
|
||||||
|
{
|
||||||
|
std::uniform_int_distribution<int> dist(0, m->size - 1);
|
||||||
|
|
||||||
|
if (aiWinCheck(m)) return;
|
||||||
|
if (humWinCheck(m)) return;
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
do {
|
||||||
|
y = dist(mt);
|
||||||
|
x = dist(mt);
|
||||||
|
} while (m->cells[x][y]->p != EMPTY);
|
||||||
|
setVal(m->cells[x][y], AI, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool aiWinCheck(map *m)
|
||||||
|
{
|
||||||
|
for (int y = 0; y < m->size; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < m->size; x++)
|
||||||
|
{
|
||||||
|
cell *c = m->cells[y][x];
|
||||||
|
|
||||||
|
if (c->p == EMPTY)
|
||||||
|
{
|
||||||
|
setVal(c, AI, true);
|
||||||
|
if (checkWin(m, AI))
|
||||||
|
return true;
|
||||||
|
setVal(c, EMPTY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool humWinCheck(map *m)
|
||||||
|
{
|
||||||
|
for (int y = 0; y < m->size; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < m->size; x++)
|
||||||
|
{
|
||||||
|
cell *c = m->cells[y][x];
|
||||||
|
|
||||||
|
if (c->p == EMPTY)
|
||||||
|
{
|
||||||
|
setVal(c, HUMAN, true);
|
||||||
|
if (checkWin(m, HUMAN))
|
||||||
|
{
|
||||||
|
setVal(c, AI, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
setVal(c, EMPTY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setVal(cell *c, PLAYER p, bool draw)
|
||||||
|
{
|
||||||
|
c->p = p;
|
||||||
|
c->is_draw = draw;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkWin(map *m, PLAYER p)
|
||||||
|
{
|
||||||
|
for (int y = 0; y < m->size; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < m->size; x++)
|
||||||
|
{
|
||||||
|
if (m->cells[y][x]->p == EMPTY) continue;
|
||||||
|
if (checkLine(m, y, x, 0, 1, m->toWin, p)) return true;
|
||||||
|
if (checkLine(m, y, x, 1, 1, m->toWin, p)) return true;
|
||||||
|
if (checkLine(m, y, x, 1, 0, m->toWin, p)) return true;
|
||||||
|
if (checkLine(m, y, x, -1, 1, m->toWin, p)) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkLine(map *m, int y, int x, int vy, int vx, int len, PLAYER p)
|
||||||
|
{
|
||||||
|
const int endX = x + (len - 1) * vx;
|
||||||
|
const int endY = y + (len - 1) * vy;
|
||||||
|
|
||||||
|
if (!isValid(m, endX, endY))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (int i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
if (m->cells[y + i * vy][x + i * vx]->p != p)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isValid(map *m, int y, int x)
|
||||||
|
{
|
||||||
|
return CHECK_DOT(y, m->size) && CHECK_DOT(x, m->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isDraw(map *m)
|
||||||
|
{
|
||||||
|
for (int y = 0; y < m->size; y++)
|
||||||
|
for (int x = 0; x < m->size; x++)
|
||||||
|
if (m->cells[y][x]->p == EMPTY)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
#ifndef AI_HPP_
|
||||||
|
#define AI_HPP_
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <random>
|
||||||
|
#include <algorithm>
|
||||||
|
#include "map.hpp"
|
||||||
|
|
||||||
|
#define CHECK_DOT(X, Y) ((X) >= 0 && (X) < (Y))
|
||||||
|
|
||||||
|
void aiTurn(map *);
|
||||||
|
bool aiWinCheck(map *);
|
||||||
|
bool humWinCheck(map *);
|
||||||
|
void setVal(cell *, PLAYER, bool = false);
|
||||||
|
bool checkWin(map *, PLAYER);
|
||||||
|
bool checkLine(map *, int, int, int, int, int, PLAYER);
|
||||||
|
bool isValid(map *, int, int);
|
||||||
|
bool isDraw(map *m);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,211 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include <allegro5/allegro.h>
|
||||||
|
#include <allegro5/allegro_native_dialog.h>
|
||||||
|
#include <allegro5/allegro_primitives.h>
|
||||||
|
#include <allegro5/allegro_image.h>
|
||||||
|
|
||||||
|
#include "ai.hpp"
|
||||||
|
#include "map.hpp"
|
||||||
|
#include "parse_args.hpp"
|
||||||
|
#include "version.hpp"
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int countKeys = 3;
|
||||||
|
|
||||||
|
ra::key ks; // size
|
||||||
|
ra::key kw; // width/height window
|
||||||
|
ra::key km; // margin map
|
||||||
|
ra::key kv; // version
|
||||||
|
|
||||||
|
ra::key *keys[countKeys] = {&ks, &kw, &km, &kv};
|
||||||
|
|
||||||
|
ra::parse_args(argc, argv, keys);
|
||||||
|
|
||||||
|
int size = 3;
|
||||||
|
int window_wh = 600;
|
||||||
|
int margin_map = 20;
|
||||||
|
|
||||||
|
if (kv.isset)
|
||||||
|
{
|
||||||
|
std::cout << "TicTacToe " << version << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ks.isset)
|
||||||
|
{
|
||||||
|
size = atoi(ks.arguments[0]);
|
||||||
|
if (size > 10 || size < 3)
|
||||||
|
size = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kw.isset)
|
||||||
|
{
|
||||||
|
window_wh = atoi(kw.arguments[0]);
|
||||||
|
if (window_wh > 1000 || window_wh < 300)
|
||||||
|
window_wh = 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (km.isset)
|
||||||
|
{
|
||||||
|
margin_map = atoi(km.arguments[0]);
|
||||||
|
if (margin_map > 100 || size < 20)
|
||||||
|
margin_map = 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
map *m = init_map(size, window_wh, margin_map);
|
||||||
|
|
||||||
|
bool done = false;
|
||||||
|
bool redraw = true;
|
||||||
|
bool isdraw = true;
|
||||||
|
int FPS = 60;
|
||||||
|
bool move_ai = false;
|
||||||
|
|
||||||
|
int mouse_x = 0;
|
||||||
|
int mouse_y = 0;
|
||||||
|
|
||||||
|
ALLEGRO_DISPLAY *display = NULL;
|
||||||
|
ALLEGRO_EVENT_QUEUE *event_queue = NULL;
|
||||||
|
ALLEGRO_TIMER *timer = NULL;
|
||||||
|
ALLEGRO_BITMAP *img_x = NULL;
|
||||||
|
ALLEGRO_BITMAP *img_o = NULL;
|
||||||
|
|
||||||
|
if (!al_init())
|
||||||
|
{
|
||||||
|
al_show_native_message_box(NULL, NULL, NULL,
|
||||||
|
"Не удается инициализировать allegro!", NULL, ALLEGRO_MESSAGEBOX_ERROR);
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
display = al_create_display(window_wh, window_wh);
|
||||||
|
|
||||||
|
if(!display)
|
||||||
|
{
|
||||||
|
al_show_native_message_box(NULL, NULL, "Ошибка!", "Не удается инициализировать дисплей!", NULL, ALLEGRO_MESSAGEBOX_ERROR);
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
al_init_primitives_addon();
|
||||||
|
al_install_keyboard();
|
||||||
|
al_install_mouse();
|
||||||
|
al_init_image_addon();
|
||||||
|
|
||||||
|
img_x = al_load_bitmap("data/x.png");
|
||||||
|
|
||||||
|
if(!img_x)
|
||||||
|
{
|
||||||
|
al_show_native_message_box(display, NULL, "Ошибка!", "Не удается инициализировать \"x.png\"", NULL, ALLEGRO_MESSAGEBOX_ERROR);
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
img_o = al_load_bitmap("data/o.png");
|
||||||
|
|
||||||
|
if(!img_o)
|
||||||
|
{
|
||||||
|
al_show_native_message_box(display, NULL, "Ошибка!", "Не удается инициализировать \"o.png\"!", NULL, ALLEGRO_MESSAGEBOX_ERROR);
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
timer = al_create_timer(1.0 / FPS);
|
||||||
|
event_queue = al_create_event_queue();
|
||||||
|
|
||||||
|
if(!event_queue)
|
||||||
|
{
|
||||||
|
al_show_native_message_box(display, NULL, "Ошибка!",
|
||||||
|
"Не удается инициализировать событие!", NULL, ALLEGRO_MESSAGEBOX_ERROR);
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
al_register_event_source(event_queue, al_get_keyboard_event_source());
|
||||||
|
al_register_event_source(event_queue, al_get_display_event_source(display));
|
||||||
|
al_register_event_source(event_queue, al_get_timer_event_source(timer));
|
||||||
|
al_register_event_source(event_queue, al_get_mouse_event_source());
|
||||||
|
|
||||||
|
al_start_timer(timer);
|
||||||
|
|
||||||
|
while (!done)
|
||||||
|
{
|
||||||
|
ALLEGRO_EVENT ev;
|
||||||
|
al_wait_for_event(event_queue, &ev);
|
||||||
|
|
||||||
|
if (ev.type == ALLEGRO_EVENT_KEY_UP)
|
||||||
|
{
|
||||||
|
switch (ev.keyboard.keycode)
|
||||||
|
{
|
||||||
|
case ALLEGRO_KEY_ESCAPE:
|
||||||
|
if (exit_game(display))
|
||||||
|
{
|
||||||
|
done = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
al_flush_event_queue(event_queue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE)
|
||||||
|
{
|
||||||
|
if (exit_game(display))
|
||||||
|
{
|
||||||
|
done = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
al_flush_event_queue(event_queue);
|
||||||
|
}
|
||||||
|
else if (ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_UP)
|
||||||
|
{
|
||||||
|
if (isdraw && enter_cell(m, mouse_x, mouse_y, HUMAN))
|
||||||
|
{
|
||||||
|
move_ai = true;
|
||||||
|
isdraw = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ev.type == ALLEGRO_EVENT_MOUSE_AXES)
|
||||||
|
{
|
||||||
|
mouse_x = ev.mouse.x;
|
||||||
|
mouse_y = ev.mouse.y;
|
||||||
|
}
|
||||||
|
else if (ev.type == ALLEGRO_EVENT_TIMER)
|
||||||
|
{
|
||||||
|
if (isdraw)
|
||||||
|
{
|
||||||
|
if (game_check(m, display))
|
||||||
|
{
|
||||||
|
al_flush_event_queue(event_queue);
|
||||||
|
done = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (move_ai)
|
||||||
|
{
|
||||||
|
aiTurn(m);
|
||||||
|
move_ai = false;
|
||||||
|
isdraw = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
select_cell(m, mouse_x, mouse_y);
|
||||||
|
|
||||||
|
redraw = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (redraw && al_is_event_queue_empty(event_queue))
|
||||||
|
{
|
||||||
|
redraw = false;
|
||||||
|
isdraw = true;
|
||||||
|
|
||||||
|
draw_map(m, img_x, img_o);
|
||||||
|
|
||||||
|
al_flip_display();
|
||||||
|
al_clear_to_color(al_map_rgb(255, 255, 255));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free_map(m);
|
||||||
|
|
||||||
|
al_destroy_display(display);
|
||||||
|
al_destroy_bitmap(img_x);
|
||||||
|
al_destroy_bitmap(img_o);
|
||||||
|
al_destroy_timer(timer);
|
||||||
|
al_destroy_event_queue(event_queue);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,256 @@
|
||||||
|
#include "map.hpp"
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <allegro5/allegro_primitives.h>
|
||||||
|
#include <allegro5/allegro_native_dialog.h>
|
||||||
|
#include "ai.hpp"
|
||||||
|
|
||||||
|
map *init_map(const int size, const int window_wh, const int margin_map)
|
||||||
|
{
|
||||||
|
map *m = (map *)malloc(sizeof(map));
|
||||||
|
|
||||||
|
float map_wh = 0; // размер карты с учётом отступов
|
||||||
|
float cell_width = 0; // ширина ячейки
|
||||||
|
float cell_margin = 0; // отступ внутри ячейки
|
||||||
|
float line_count = 0; // количество линий на одно направление
|
||||||
|
float line_width = 0; // ширина линии
|
||||||
|
float cell_sym_width = 0; // ширина сивола внутри ячейки
|
||||||
|
|
||||||
|
m->size = size;
|
||||||
|
m->window_wh = window_wh;
|
||||||
|
m->margin_map = margin_map;
|
||||||
|
m->toWin = size;
|
||||||
|
|
||||||
|
line_count = size - 1;
|
||||||
|
map_wh = window_wh - margin_map * 2;
|
||||||
|
line_width = map_wh * 0.0036;
|
||||||
|
cell_width = map_wh / size;
|
||||||
|
cell_margin = cell_width * 0.0538;
|
||||||
|
cell_sym_width = cell_width - (line_width * 2) - (cell_margin * 2);
|
||||||
|
|
||||||
|
m->sym_width = cell_sym_width;
|
||||||
|
|
||||||
|
m->cells = (cell ***)malloc(sizeof(cell **) * (size * size));
|
||||||
|
|
||||||
|
for (int i = 0; i < size; ++i)
|
||||||
|
{
|
||||||
|
m->cells[i] = (cell **)malloc(sizeof(cell *) * size);
|
||||||
|
|
||||||
|
for (int j = 0; j < size; ++j)
|
||||||
|
{
|
||||||
|
m->cells[i][j] = create_cell(j, i, cell_width, cell_margin, line_width, margin_map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m->grid = (line ***)malloc(sizeof(line **) * (line_count * line_count));
|
||||||
|
|
||||||
|
for (int i = 0; i < 2; ++i)
|
||||||
|
{
|
||||||
|
m->grid[i] = (line **)malloc(sizeof(line *) * line_count);
|
||||||
|
|
||||||
|
DIRECTION d = (i == 0 ? HORIZONTAL : VERTICAL);
|
||||||
|
|
||||||
|
for (int j = 0; j < line_count; ++j)
|
||||||
|
{
|
||||||
|
m->grid[i][j] = create_line(d, j, line_width, map_wh, cell_width, margin_map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
cell *create_cell(const float row, const float col, const float cell_width, const float cell_margin, const float line_width, const float margin_map)
|
||||||
|
{
|
||||||
|
cell *c = (cell *)malloc(sizeof(cell));
|
||||||
|
c->select = false;
|
||||||
|
c->is_draw = false;
|
||||||
|
c->pos_x = row * cell_width + margin_map;
|
||||||
|
c->pos_y = col * cell_width + margin_map;
|
||||||
|
c->width = cell_width;
|
||||||
|
c->sym_pos_x = c->pos_x + line_width + cell_margin;
|
||||||
|
c->sym_pos_y = c->pos_y + line_width + cell_margin;
|
||||||
|
c->p = EMPTY;
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
line *create_line(DIRECTION d, const float row, const float line_width, const float map_wh, const float cell_width, const float margin_map)
|
||||||
|
{
|
||||||
|
line *l = (line *)malloc(sizeof(line));
|
||||||
|
l->d = d;
|
||||||
|
l->height = map_wh;
|
||||||
|
l->width = line_width;
|
||||||
|
if (d == HORIZONTAL)
|
||||||
|
{
|
||||||
|
l->pos_x = margin_map;
|
||||||
|
l->pos_y = (row + 1) * cell_width + margin_map - line_width;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
l->pos_x = (row + 1) * cell_width + margin_map - line_width;
|
||||||
|
l->pos_y = margin_map;
|
||||||
|
}
|
||||||
|
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_map(const map *m, ALLEGRO_BITMAP *bx, ALLEGRO_BITMAP *bo)
|
||||||
|
{
|
||||||
|
int size = m->size;
|
||||||
|
|
||||||
|
int swh = al_get_bitmap_width(bx);
|
||||||
|
|
||||||
|
for (int i = 0; i < size; ++i)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < size; ++j)
|
||||||
|
{
|
||||||
|
cell *c = m->cells[i][j];
|
||||||
|
|
||||||
|
if (c->is_draw)
|
||||||
|
{
|
||||||
|
if (c->p == HUMAN)
|
||||||
|
al_draw_scaled_bitmap(bx, 0, 0, swh, swh, c->sym_pos_x, c->sym_pos_y, m->sym_width, m->sym_width, 0);
|
||||||
|
else
|
||||||
|
al_draw_scaled_bitmap(bo, 0, 0, swh, swh, c->sym_pos_x, c->sym_pos_y, m->sym_width, m->sym_width, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c->select)
|
||||||
|
{
|
||||||
|
if (c->is_draw)
|
||||||
|
al_draw_tinted_scaled_bitmap(bx, al_map_rgba_f(255, 0, 0, 0.3), 0, 0, swh, swh, c->sym_pos_x, c->sym_pos_y, m->sym_width, m->sym_width, 0);
|
||||||
|
else
|
||||||
|
al_draw_tinted_scaled_bitmap(bx, al_map_rgba_f(0, 255, 0, 0.3), 0, 0, swh, swh, c->sym_pos_x, c->sym_pos_y, m->sym_width, m->sym_width, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 2; ++i)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < size - 1; ++j)
|
||||||
|
{
|
||||||
|
line *l = m->grid[i][j];
|
||||||
|
|
||||||
|
if (l->d == HORIZONTAL)
|
||||||
|
al_draw_filled_rectangle(l->pos_x, l->pos_y, l->pos_x + l->height, l->pos_y + l->width, al_map_rgb(0, 0, 0));
|
||||||
|
else
|
||||||
|
al_draw_filled_rectangle(l->pos_x, l->pos_y, l->pos_x + l->width, l->pos_y + l->height, al_map_rgb(0, 0, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void select_cell(map *m, const int mouse_x, const int mouse_y)
|
||||||
|
{
|
||||||
|
int size = m->size;
|
||||||
|
|
||||||
|
for (int i = 0; i < size; ++i)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < size; ++j)
|
||||||
|
{
|
||||||
|
cell *c = m->cells[i][j];
|
||||||
|
|
||||||
|
if ((mouse_x >= c->pos_x && mouse_y >= c->pos_y) &&
|
||||||
|
(mouse_x <= (c->pos_x + c->width) && mouse_y <= (c->pos_y + c->width)) && c->p != HUMAN)
|
||||||
|
c->select = true;
|
||||||
|
else
|
||||||
|
c->select = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool enter_cell(map *m, const int mouse_x, const int mouse_y, PLAYER p)
|
||||||
|
{
|
||||||
|
int size = m->size;
|
||||||
|
|
||||||
|
for (int i = 0; i < size; ++i)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < size; ++j)
|
||||||
|
{
|
||||||
|
cell *c = m->cells[i][j];
|
||||||
|
|
||||||
|
if (!c->is_draw && (mouse_x >= c->pos_x && mouse_y >= c->pos_y) &&
|
||||||
|
(mouse_x <= (c->pos_x + c->width) && mouse_y <= (c->pos_y + c->width)))
|
||||||
|
{
|
||||||
|
c->is_draw = true;
|
||||||
|
c->p = p;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear_map(map *m)
|
||||||
|
{
|
||||||
|
int size = m->size;
|
||||||
|
|
||||||
|
for (int i = 0; i < size; ++i)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < size; ++j)
|
||||||
|
{
|
||||||
|
cell *c = m->cells[i][j];
|
||||||
|
|
||||||
|
c->is_draw = false;
|
||||||
|
c->select = false;
|
||||||
|
c->p = EMPTY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool game_check(map *m, ALLEGRO_DISPLAY *d)
|
||||||
|
{
|
||||||
|
int answer = -1;
|
||||||
|
|
||||||
|
if (checkWin(m, HUMAN))
|
||||||
|
answer = al_show_native_message_box(d, "Игра окончена!", "Вы победили!",
|
||||||
|
"Начать игру сначала?", NULL, ALLEGRO_MESSAGEBOX_YES_NO);
|
||||||
|
|
||||||
|
if (checkWin(m, AI))
|
||||||
|
answer = al_show_native_message_box(d, "Игра окончена!", "Вы проиграли!",
|
||||||
|
"Начать игру сначала?", NULL, ALLEGRO_MESSAGEBOX_YES_NO);
|
||||||
|
|
||||||
|
if (isDraw(m))
|
||||||
|
answer = al_show_native_message_box(d, "Игра окончена!", "Ничья!",
|
||||||
|
"Начать игру сначала?", NULL, ALLEGRO_MESSAGEBOX_YES_NO);
|
||||||
|
|
||||||
|
if (answer == 1)
|
||||||
|
clear_map(m);
|
||||||
|
else if (answer == 0 || answer == 2)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_map(map *m)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < m->size; ++i)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < m->size; ++j)
|
||||||
|
{
|
||||||
|
free(m->cells[i][j]);
|
||||||
|
}
|
||||||
|
free(m->cells[i]);
|
||||||
|
}
|
||||||
|
free(m->cells);
|
||||||
|
|
||||||
|
for (int i = 0; i < 2; ++i)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < m->size - 1; ++j)
|
||||||
|
{
|
||||||
|
free(m->grid[i][j]);
|
||||||
|
}
|
||||||
|
free(m->grid[i]);
|
||||||
|
}
|
||||||
|
free(m->grid);
|
||||||
|
|
||||||
|
free(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool exit_game(ALLEGRO_DISPLAY *d)
|
||||||
|
{
|
||||||
|
int answer = al_show_native_message_box(d, NULL, "Выход из игры",
|
||||||
|
"Вы хотите закончить игру?", NULL, ALLEGRO_MESSAGEBOX_YES_NO);
|
||||||
|
|
||||||
|
if (answer == 1)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
#ifndef MAP_HPP_
|
||||||
|
#define MAP_HPP_
|
||||||
|
|
||||||
|
#include <allegro5/allegro.h>
|
||||||
|
|
||||||
|
typedef enum {HUMAN, AI, EMPTY} PLAYER;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
bool is_draw;
|
||||||
|
bool select;
|
||||||
|
float pos_x;
|
||||||
|
float pos_y;
|
||||||
|
float width;
|
||||||
|
float sym_pos_x;
|
||||||
|
float sym_pos_y;
|
||||||
|
PLAYER p;
|
||||||
|
} cell;
|
||||||
|
|
||||||
|
typedef enum {HORIZONTAL, VERTICAL} DIRECTION;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
DIRECTION d;
|
||||||
|
float pos_x;
|
||||||
|
float pos_y;
|
||||||
|
float width;
|
||||||
|
float height;
|
||||||
|
} line;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cell ***cells;
|
||||||
|
line ***grid;
|
||||||
|
int toWin;
|
||||||
|
int size;
|
||||||
|
int window_wh;
|
||||||
|
int margin_map;
|
||||||
|
int sym_width;
|
||||||
|
} map;
|
||||||
|
|
||||||
|
map *init_map(const int, const int, const int);
|
||||||
|
cell *create_cell(const float, const float, const float, const float, const float, const float);
|
||||||
|
line *create_line(DIRECTION, const float, const float, const float, const float, const float);
|
||||||
|
void draw_map(const map *, ALLEGRO_BITMAP *, ALLEGRO_BITMAP *);
|
||||||
|
void select_cell(map *, const int, const int);
|
||||||
|
bool enter_cell(map *, const int, const int, PLAYER);
|
||||||
|
bool game_check(map *, ALLEGRO_DISPLAY *);
|
||||||
|
void clear_map(map *);
|
||||||
|
void free_map(map *);
|
||||||
|
bool exit_game(ALLEGRO_DISPLAY *);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,53 @@
|
||||||
|
#include "parse_args.hpp"
|
||||||
|
|
||||||
|
[[ noreturn ]] void ra::print_usage_and_exit(int code)
|
||||||
|
{
|
||||||
|
puts("Использование: tictactoe [option] [arguments] ...\n");
|
||||||
|
puts(" -h, --help Получить информацию об использовании");
|
||||||
|
puts(" -s, --size <count columns> Размер сетки N*N");
|
||||||
|
puts(" -w, --width <size> Ширина/высота игрового окна");
|
||||||
|
puts(" -m, --margin <size> Размер внутреннего отступа от границы окна до игрового поля");
|
||||||
|
puts(" -v, --version Версия TicTacToe\n");
|
||||||
|
exit(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ra::parse_args(int argc, char *argv[], key **keys)
|
||||||
|
{
|
||||||
|
int next_option = 0;
|
||||||
|
|
||||||
|
do{
|
||||||
|
next_option = getopt_long(argc, argv, short_options, long_options, nullptr);
|
||||||
|
|
||||||
|
switch(next_option)
|
||||||
|
{
|
||||||
|
case 's':
|
||||||
|
ra::get_argument(keys[ksize]);
|
||||||
|
break;
|
||||||
|
case 'w':
|
||||||
|
ra::get_argument(keys[kwidth]);
|
||||||
|
break;
|
||||||
|
case 'm':
|
||||||
|
ra::get_argument(keys[kmargin]);
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
ra::get_argument(keys[kversion]);
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
ra::print_usage_and_exit(0);
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
|
ra::print_usage_and_exit(1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (next_option != -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ra::get_argument(key *curKey)
|
||||||
|
{
|
||||||
|
if (curKey->isset)
|
||||||
|
ra::print_usage_and_exit(3);
|
||||||
|
|
||||||
|
curKey->arguments[0] = optarg;
|
||||||
|
curKey->isset = true;
|
||||||
|
curKey->count = 1;
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
#ifndef PARSE_ARGS_HPP_
|
||||||
|
#define PARSE_ARGS_HPP_
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <getopt.h>
|
||||||
|
|
||||||
|
namespace ra // read arguments
|
||||||
|
{
|
||||||
|
enum keys {ksize, kwidth, kmargin, kversion};
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
bool required = false; // Ключ является обязательным для установки
|
||||||
|
bool isset = false; // Ключ был установлен при запуске программы
|
||||||
|
int count = 0; // Количество аргументов переданных для текущего ключа
|
||||||
|
char *arguments[1] = {nullptr}; // Переданные аргументы (до 10 аругментов на один ключ)
|
||||||
|
} key;
|
||||||
|
|
||||||
|
const char* const short_options = "hs:w:m:v";
|
||||||
|
|
||||||
|
const struct option long_options[] =
|
||||||
|
{
|
||||||
|
{ "help", 0, nullptr, 'h'},
|
||||||
|
{ "size", 1, nullptr, 's'},
|
||||||
|
{ "width", 1, nullptr, 'w'},
|
||||||
|
{ "margin", 1, nullptr, 'm'},
|
||||||
|
{ "version", 0, nullptr, 'v'},
|
||||||
|
{ nullptr, 0, nullptr, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
[[ noreturn ]] void print_usage_and_exit(int); // Напечатать справку и выйти с кодом ошибки
|
||||||
|
void parse_args(int, char **, key **); // Прочитать все ключи
|
||||||
|
void get_argument(key *); // Получить аргумент ключа
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,3 @@
|
||||||
|
#include "version.hpp"
|
||||||
|
|
||||||
|
std::string version = "v1.0.0";
|
|
@ -0,0 +1,8 @@
|
||||||
|
#ifndef VERSION_HPP_
|
||||||
|
#define VERSION_HPP_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
extern std::string version;
|
||||||
|
|
||||||
|
#endif
|
Binary file not shown.
After Width: | Height: | Size: 33 KiB |
Loading…
Reference in New Issue