Fragment code into multiple files
This commit is contained in:
parent
b6b296d099
commit
74c2eb03e6
6 changed files with 282 additions and 244 deletions
12
Makefile
12
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
|
||||
|
|
121
block.cpp
Normal file
121
block.cpp
Normal file
|
@ -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<int> 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<int> 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<int> 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<tile> *r) {
|
||||
if(depth == 0)
|
||||
r->push_back(t);
|
||||
else
|
||||
for(Block* son : sons)
|
||||
son->save(r);
|
||||
}
|
||||
void Block::load_state(vector<tile> 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<int> &prev) {
|
||||
// TODO
|
||||
}
|
72
global.hpp
Normal file
72
global.hpp
Normal file
|
@ -0,0 +1,72 @@
|
|||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <string>
|
||||
#include <raylib.h>
|
||||
|
||||
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<Block*, 9> sons;
|
||||
|
||||
Block() {}
|
||||
Block(int _depth, Rectangle trg);
|
||||
Rectangle get_son_rectangle(int son);
|
||||
int set(tile pl);
|
||||
bool playable(deque<int> v);
|
||||
int play(deque<int> v, tile pl);
|
||||
bool check_tie();
|
||||
tile update();
|
||||
Rectangle getRect(deque<int> v);
|
||||
void updateRect(Rectangle trg);
|
||||
|
||||
void save(vector<tile> *r);
|
||||
void load_state(vector<tile> state, int pos);
|
||||
void undo(deque<int> &prev);
|
||||
};
|
||||
|
||||
void update(int &time, deque<int> &turn, Block* root, Rectangle &dst);
|
||||
void undo(Block *root, deque<int> &prev, Rectangle &dst, int &time);
|
||||
bool valid(Block* root, Rectangle &dst, deque<int> &turn);
|
||||
|
||||
class Renderer {
|
||||
array<Texture2D, IMG_COUNT> 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<int> highlight = {});
|
||||
void render_block(Block *b);
|
||||
int update_board_size(int depth);
|
||||
void prepare_screen(Block* root, int time, Rectangle dst);
|
||||
};
|
59
graphics.cpp
Normal file
59
graphics.cpp
Normal file
|
@ -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<int> 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);
|
||||
}
|
||||
|
||||
|
BIN
piskvorky
Executable file
BIN
piskvorky
Executable file
Binary file not shown.
262
recursive.cpp
262
recursive.cpp
|
@ -1,72 +1,10 @@
|
|||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <string>
|
||||
#include <raylib.h>
|
||||
#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<Texture2D, IMG_COUNT> 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<int> to_deque(int depth, int screenSize) {
|
||||
deque<int> 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<int> to_deque(Vector2 v, int depth, int size) {
|
||||
deque<int> r = {};
|
||||
|
@ -84,148 +22,6 @@ deque<int> 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<Block*, 9> 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<int> 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<int> 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<int> 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<tile> *r) {
|
||||
if(depth == 0)
|
||||
r->push_back(t);
|
||||
else // TODO save?
|
||||
for(Block* son : sons)
|
||||
son->save(r);
|
||||
}
|
||||
void load_state(vector<tile> 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<int> &prev) {
|
||||
// TODall: $(PAGES) ...O
|
||||
}
|
||||
};
|
||||
|
||||
void save_state(Block* root, int depth, int time, deque<int> &turn) {
|
||||
vector<tile> state = {};
|
||||
|
@ -236,9 +32,11 @@ void save_state(Block* root, int depth, int time, deque<int> &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<int> &prev, Rectangle &dst, int &time) {
|
|||
bool valid(Block* root, Rectangle &dst, deque<int> &turn) {
|
||||
return CheckCollisionRecs(root->getRect(turn), dst) && root->playable(turn);
|
||||
}
|
||||
void render_all(Block* root, int time, Rectangle dst, deque<int> 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<int> 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;
|
||||
|
|
Loading…
Reference in a new issue