diff --git a/Makefile b/Makefile index 06595e8..78cc18a 100644 --- a/Makefile +++ b/Makefile @@ -1,2 +1,10 @@ -all: recursive.cpp - g++ $< -Iraylib -lraylib -o piskvorky +CPP_FILES = \ + graphics.cpp \ + block.cpp \ + recursive.cpp + +HPP_FILES = \ + global.hpp + +all: $(CPP_FILES) $(HPP_FILES) + g++ $(CPP_FILES) -Iraylib -lraylib -o piskvorky diff --git a/block.cpp b/block.cpp new file mode 100644 index 0000000..a2aecbe --- /dev/null +++ b/block.cpp @@ -0,0 +1,121 @@ +#include "global.hpp" + +Block::Block(int _depth, Rectangle trg) { + place = trg; + int x = trg.x, y = trg.y, w = trg.width, h = trg.height; + depth = _depth; + if(depth > 0) + for(int i = 0; i < 9; i++) + sons[i] = new Block( depth-1, get_son_rectangle(i) ); +} +Rectangle Block::get_son_rectangle(int son) { + return { + float(int(place.x + place.width/3 * (son%3))), + float(int(place.y + place.height/3 * int(son/3))), + float(int(place.width/3)), + float(int(place.height/3)) + }; +} +int Block::set(tile pl) { + t = pl; + if(pl == empty) + return OKAY; + return CHANGE; +} +bool Block::playable(deque v) { + if(depth == 0 && t == empty) + return true; + if(t != empty || v.size() == 0) + return false; + int son = v.front(); + v.pop_front(); + return sons[son]->playable(v); +} +int Block::play(deque v, tile pl) { + if(t != empty) + return v.size(); + if(depth == 0) + return set(pl); + if(v.size() == 0) + return OKAY; + + int son = v.front(); + v.erase(v.begin()); + int rv = sons[son]->play(v, pl); + + if(rv == CHANGE) + return set(update()); + return rv; +} +bool Block::check_tie() { + bool is_tie = true; + for(Block* s : sons) + if(s->t == empty) + is_tie = false; + return is_tie; +} +tile Block::update() { + tile d1 = sons[0]->t, d2 = sons[2]->t; + for(int k = 0; k < 3; k++) { + tile row = sons[k*3]->t, col = sons[k%3]->t; + for(int i = 0; i < 3; i++) { + if(sons[k*3 + i]->t != row) + row = empty; + if(sons[k%3 + i*3]->t != col) + col = empty; + } + if(row != empty) + return row; + if(col != empty) + return col; + if(sons[k*4]->t != sons[0]->t) + d1 = empty; + if(sons[(k+1)*2]->t != sons[2]->t) + d2 = empty; + } + if(d1 != empty) + return d1; + if(d2 != empty) + return d2; + + if(check_tie()) + return tie; + + return empty; +} +Rectangle Block::getRect(deque v) { + if(v.size() == 0) + return place; + else { + int index = v.front(); + v.pop_front(); + return sons[index]->getRect(v); + } +} +void Block::updateRect(Rectangle trg) { + place = trg; + if(depth == 0) + return; + for(int i = 0; i < 9; i++) + sons[i]->updateRect(get_son_rectangle(i)); +} + +void Block::save(vector *r) { + if(depth == 0) + r->push_back(t); + else + for(Block* son : sons) + son->save(r); +} +void Block::load_state(vector state, int pos) { + if(depth == 0) + t = state[pos]; + else { + for(int i = 0; i < 9; i++) + sons[i]->load_state(state, pos*9+i); + t = update(); + } +} +void Block::undo(deque &prev) { + // TODO +} diff --git a/global.hpp b/global.hpp new file mode 100644 index 0000000..7ab5f28 --- /dev/null +++ b/global.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +using std::deque; +using std::string; +using std::array; +using std::vector; + +using std::cin; +using std::cout; + +#define FPS 15 +#define SCREEN 800 +#define LINE_W 2 + +#define IMG_COUNT 2 + +enum tile {empty = 0, p1 = 1, p2 = 2, tie = 3}; +#define OKAY -1 +#define CHANGE -2 + + +tile act(int t); +tile pas(int t); +Color col(int t); + +struct Block { + tile t = empty; + int depth; + Rectangle place; + array sons; + + Block() {} + Block(int _depth, Rectangle trg); + Rectangle get_son_rectangle(int son); + int set(tile pl); + bool playable(deque v); + int play(deque v, tile pl); + bool check_tie(); + tile update(); + Rectangle getRect(deque v); + void updateRect(Rectangle trg); + + void save(vector *r); + void load_state(vector state, int pos); + void undo(deque &prev); +}; + +void update(int &time, deque &turn, Block* root, Rectangle &dst); +void undo(Block *root, deque &prev, Rectangle &dst, int &time); +bool valid(Block* root, Rectangle &dst, deque &turn); + +class Renderer { + array textures; + Block *root; + + void prepareTextures(); + +public: + void renderImage(tile t, Rectangle dst, Color color = WHITE); + void render_all(Block* root, int time, Rectangle play_area, deque highlight = {}); + void render_block(Block *b); + int update_board_size(int depth); + void prepare_screen(Block* root, int time, Rectangle dst); +}; diff --git a/graphics.cpp b/graphics.cpp new file mode 100644 index 0000000..b68dc28 --- /dev/null +++ b/graphics.cpp @@ -0,0 +1,59 @@ +#include "global.hpp" + + +// Rendering +void Renderer::prepareTextures() { + const char* paths[] = {"img/circle.png", "img/cross.png"}; + for(int i = 0; i < IMG_COUNT; i++) { + Image img; + img = LoadImage(paths[i]); + textures[i] = LoadTextureFromImage(img); + } +} +void Renderer::renderImage(tile t, Rectangle dst, Color color) { + if(t == empty || t == tie) + return; + Texture tex = textures[t-1]; + Rectangle src = {0, 0, (float)tex.width, (float)tex.height}; + DrawTexturePro(textures[t-1], src, dst, {0,0}, 0, color); +} +void Renderer::render_block(Block *b) { + if(b->depth > 0) + DrawRectangleLinesEx(b->place, b->depth, BLACK); + renderImage(b->t, b->place); + if(b->depth == 0 || (b->t != empty && b->t != tie)) + return; + + for(int i = 0; i < 9; i++) + render_block(b->sons[i]); +} +void Renderer::render_all(Block* root, int time, Rectangle play_area, deque highlight) { + BeginDrawing(); + ClearBackground(RAYWHITE); + + render_block(root); + if(valid(root, play_area, highlight)) + renderImage(act(time), root->getRect(highlight), BLACK); + DrawRectangleLinesEx(play_area, 3*LINE_W, col(time+1)); + + EndDrawing(); +} +int Renderer::update_board_size(int depth) { + int max_size = std::min(GetScreenWidth(), GetScreenHeight()); + + int size = 1; + for(int i = 0; i < depth; i++) + size *= 3; + + max_size -= max_size % size; + return max_size; +} +void Renderer::prepare_screen(Block* root, int time, Rectangle dst) { + SetConfigFlags(FLAG_WINDOW_RESIZABLE); + InitWindow(SCREEN, SCREEN, "Rekurze"); + SetTargetFPS(FPS); + prepareTextures(); + render_all(root, time, dst); +} + + diff --git a/piskvorky b/piskvorky new file mode 100755 index 0000000..eb8971a Binary files /dev/null and b/piskvorky differ diff --git a/recursive.cpp b/recursive.cpp index 937efc3..06a31b5 100644 --- a/recursive.cpp +++ b/recursive.cpp @@ -1,72 +1,10 @@ -#include -#include -#include -#include -#include -#include -#include +#include "global.hpp" -using std::deque; -using std::string; -using std::array; -using std::vector; -using std::cin; -using std::cout; - -enum tile {empty = 0, p1 = 1, p2 = 2, tie = 3}; tile act(int t) { return (t % 2) ? p2 : p1; } tile pas(int t) { return (t % 2) ? p1 : p2; } Color col(int t) { return act(t) == p2 ? BLUE : RED; } -#define FPS 15 -#define SCREEN 800 -#define LINE_W 2 - -// Images -#define IMG_COUNT 2 -array textures; -void prepareTextures() { - const char* paths[] = {"img/circle.png", "img/cross.png"}; - for(int i = 0; i < IMG_COUNT; i++) { - Image img; - img = LoadImage(paths[i]); - textures[i] = LoadTextureFromImage(img); - } -} -void renderImage(tile t, Rectangle dst, Color color = WHITE) { - if(t == empty || t == tie) - return; - Texture tex = textures[t-1]; - Rectangle src = {0, 0, (float)tex.width, (float)tex.height}; - DrawTexturePro(textures[t-1], src, dst, {0,0}, 0, color); -} - -struct pos { - int x, y; - pos() : x(-1), y(-1) {} - pos(int px, int py) : x(px), y(py) {} - pos(Vector2 v) : x(v.x), y(v.y) {} - - pos operator/(int a) {return {x/a, y/a};}; - pos operator%(int a) {return {x%a, y%a};}; - - int to_int() { return 3*y + x; } - deque to_deque(int depth, int screenSize) { - deque r = {}; - pos pp = pos(x,y); - if(pp.x > screenSize || pp.y > screenSize) - return {}; - - for(int i = 0; i < depth; ++i) { - screenSize /= 3; - pos ppos = pp/screenSize; - r.push_back(ppos.to_int()); - pp = pp%screenSize; - } - return r; - } -}; deque to_deque(Vector2 v, int depth, int size) { deque r = {}; @@ -84,148 +22,6 @@ deque to_deque(Vector2 v, int depth, int size) { return r; } -#define OKAY -1 -#define CHANGE -2 -class Block { - tile t = empty; - Rectangle place; - int depth; - array sons; - -public: - Block() {} - Block(int _depth, Rectangle trg) { - place = trg; - int x = trg.x, y = trg.y, w = trg.width, h = trg.height; - depth = _depth; - if(depth > 0) - for(int i = 0; i < 9; i++) - sons[i] = new Block( depth-1, get_son_rectangle(i) ); - } - Rectangle get_son_rectangle(int son) { - return { - float(int(place.x + place.width/3 * (son%3))), - float(int(place.y + place.height/3 * int(son/3))), - float(int(place.width/3)), - float(int(place.height/3)) - }; - } - int change(tile pl) { - t = pl; - if(pl == empty) - return OKAY; - return CHANGE; - } - bool playable(deque v) { - if(depth == 0 && t == empty) - return true; - if(t != empty || v.size() == 0) - return false; - int son = v.front(); - v.pop_front(); - return sons[son]->playable(v); - } - int play(deque v, tile pl) { - if(t != empty) - return v.size(); - if(depth == 0) - return change(pl); - if(v.size() == 0) - return OKAY; - - int son = v.front(); - v.erase(v.begin()); - int rv = sons[son]->play(v, pl); - - if(rv == CHANGE) - return change(check_win()); - return rv; - } - bool check_tie() { - bool is_tie = true; - for(Block* s : sons) - if(s->t == empty) - is_tie = false; - return is_tie; - } - tile check_win() { - tile d1 = sons[0]->t, d2 = sons[2]->t; - for(int k = 0; k < 3; k++) { - tile row = sons[k*3]->t, col = sons[k%3]->t; - for(int i = 0; i < 3; i++) { - if(sons[k*3 + i]->t != row) - row = empty; - if(sons[k%3 + i*3]->t != col) - col = empty; - } - if(row != empty) - return row; - if(col != empty) - return col; - if(sons[k*4]->t != sons[0]->t) - d1 = empty; - if(sons[(k+1)*2]->t != sons[2]->t) - d2 = empty; - } - if(d1 != empty) - return d1; - if(d2 != empty) - return d2; - - if(check_tie()) - return tie; - - return empty; - } - void render() { - if(depth > 0) - DrawRectangleLinesEx(place, depth, BLACK); - renderImage(t, place); - if(depth == 0 || (t != empty && t != tie)) - return; - - for(int i = 0; i < 9; i++) - sons[i]->render(); - } - Rectangle getRect(deque v) { - if(v.size() == 0) - return place; - else { - int index = v.front(); - v.pop_front(); - return sons[index]->getRect(v); - } - } - void updateRect(Rectangle trg) { - place = trg; - int x = trg.x, y = trg.y, - w = trg.width, h = trg.height; - if(depth == 0) - return; - for(int i = 0; i < 9; i++) - sons[i]->updateRect(get_son_rectangle(i)); - } - - void save(vector *r) { - if(depth == 0) - r->push_back(t); - else // TODO save? - for(Block* son : sons) - son->save(r); - } - void load_state(vector state, int pos) { - if(depth == 0) - t = state[pos]; - else { - for(int i = 0; i < 9; i++) - sons[i]->load_state(state, pos*9+i); - t = check_win(); - } - } - void undo(deque &prev) { - // TODall: $(PAGES) ...O - } -}; void save_state(Block* root, int depth, int time, deque &turn) { vector state = {}; @@ -236,9 +32,11 @@ void save_state(Block* root, int depth, int time, deque &turn) { outfile << depth << "\n" << time << "\n" << turn.size() << "\n"; + // Save turns for(int play : turn) outfile << play << std::endl; + // Save game state for(tile t : state) outfile << int(t) << std::endl; } @@ -285,24 +83,6 @@ void undo(Block *root, deque &prev, Rectangle &dst, int &time) { bool valid(Block* root, Rectangle &dst, deque &turn) { return CheckCollisionRecs(root->getRect(turn), dst) && root->playable(turn); } -void render_all(Block* root, int time, Rectangle dst, deque highlight = {}) { - BeginDrawing(); - ClearBackground(RAYWHITE); - - root->render(); - if(valid(root, dst, highlight)) - renderImage(act(time), root->getRect(highlight), BLACK); - DrawRectangleLinesEx(dst, 3*LINE_W, col(time+1)); - - EndDrawing(); -} -void prepareScreen(Block* root, int time, Rectangle dst, int depth) { - SetConfigFlags(FLAG_WINDOW_RESIZABLE); - InitWindow(SCREEN, SCREEN, "Rekurze"); - SetTargetFPS(FPS); - prepareTextures(); - render_all(root, time, dst); -} int main(int argc, char** argv) { @@ -310,44 +90,41 @@ int main(int argc, char** argv) { int time = 0, depth; Block* root = nullptr; - int screenSize = SCREEN; - if(argc <= 1) { cin >> depth; - root = new Block(depth, {0, 0, (float)screenSize, (float)screenSize}); + root = new Block(depth, {0, 0, SCREEN, SCREEN}); } else load_state(&root, turn, depth, time, argv[1]); - Rectangle dst = root->getRect(turn); + Rectangle play_area = root->getRect(turn); - prepareScreen(root, time, dst, depth); + Renderer renderer; + renderer.prepare_screen(root, time, play_area); + int board_size = renderer.update_board_size(depth); bool end = false; deque pturn = {}; while(!WindowShouldClose()) { // Update screen size - screenSize = std::min(GetScreenWidth(), GetScreenHeight()); - int size = 1; - while(size <= screenSize) - size *= 3; - screenSize = size/3; - float start_x = ((float)GetScreenWidth()-screenSize)/2; - float start_y = ((float)GetScreenHeight()-screenSize)/2; + board_size = renderer.update_board_size(depth); - root->updateRect({start_x, start_y, (float)screenSize, (float)screenSize}); - dst = root->getRect(pturn); + float start_x = ((float)GetScreenWidth()-board_size)/2; + float start_y = ((float)GetScreenHeight()-board_size)/2; + + root->updateRect({start_x, start_y, (float)board_size, (float)board_size}); + play_area = root->getRect(pturn); // Move Vector2 touch = GetMousePosition(); - turn = to_deque({touch.x - start_x, touch.y - start_y}, depth, screenSize); - if(IsMouseButtonPressed(0) && CheckCollisionPointRec(touch, dst) && valid(root, dst, turn)) { + turn = to_deque({touch.x - start_x, touch.y - start_y}, depth, board_size); + if(IsMouseButtonPressed(0) && CheckCollisionPointRec(touch, play_area) && valid(root, play_area, turn)) { switch(root->play(turn, act(time))) { case CHANGE: end = true; break; case OKAY: - update(time, turn, root, dst); + update(time, turn, root, play_area); pturn = turn; break; } @@ -356,10 +133,11 @@ int main(int argc, char** argv) { // Key presses if(IsKeyPressed(KEY_X)) save_state(root, depth, time, turn), end = true; - else if(IsKeyPressed(KEY_Z)) {} //TODO undo + else if(IsKeyPressed(KEY_Z)) + {} // TODO undo // Rendering - render_all(root, time, dst, turn); + renderer.render_all(root, time, play_area, turn); if(end) break;