Skip to content

Commit 74fff4d

Browse files
committed
add implementation for 01-beginner
1 parent eaf7891 commit 74fff4d

File tree

2 files changed

+276
-0
lines changed

2 files changed

+276
-0
lines changed

01-beginner/CMakeLists.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
cmake_minimum_required(VERSION 3.10)
2+
3+
project(SOKOBAN01
4+
VERSION 0.1.0
5+
LANGUAGES CXX
6+
)
7+
8+
find_package(gf REQUIRED COMPONENTS graphics)
9+
10+
add_executable(sokoban01
11+
code/sokoban01.cc
12+
)
13+
14+
target_link_libraries(sokoban01
15+
PRIVATE
16+
gf::graphics
17+
)

01-beginner/code/sokoban01.cc

Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
#include <vector>
2+
3+
#include <gf/Array2D.h>
4+
#include <gf/Color.h>
5+
#include <gf/Direction.h>
6+
#include <gf/Event.h>
7+
#include <gf/RenderWindow.h>
8+
#include <gf/Shapes.h>
9+
#include <gf/Vector.h>
10+
#include <gf/Window.h>
11+
12+
namespace skb {
13+
14+
const char LevelData[] = {
15+
" #####"
16+
"###@ .#"
17+
"# $ #.#"
18+
"# $$ #"
19+
"#. # #"
20+
"# $.#"
21+
"#######"
22+
};
23+
24+
constexpr gf::Vector2i LevelSize = gf::vec(7, 7);
25+
26+
struct Hero {
27+
gf::Vector2i position;
28+
};
29+
30+
Hero makeHero(gf::Span<const char> data, gf::Vector2i size) {
31+
Hero hero = { gf::vec(0, 0) };
32+
gf::Vector2i position = gf::vec(0, 0);
33+
34+
for (auto c : data) {
35+
if (c == '@' || c == '+') {
36+
hero.position = position;
37+
}
38+
39+
++position.x;
40+
41+
if (position.x == size.width) {
42+
position.x = 0;
43+
++position.y;
44+
}
45+
}
46+
47+
return hero;
48+
}
49+
50+
struct Boxes {
51+
std::vector<gf::Vector2i> positions;
52+
53+
bool hasBox(gf::Vector2i coordinates) const {
54+
for (auto position : positions) {
55+
if (position == coordinates) {
56+
return true;
57+
}
58+
}
59+
60+
return false;
61+
}
62+
63+
void changeBox(gf::Vector2i from, gf::Vector2i to) {
64+
for (auto & position : positions) {
65+
if (position == from) {
66+
position = to;
67+
return;
68+
}
69+
}
70+
71+
assert(false);
72+
}
73+
};
74+
75+
Boxes makeBoxes(gf::Span<const char> data, gf::Vector2i size) {
76+
Boxes boxes;
77+
gf::Vector2i position = gf::vec(0, 0);
78+
79+
for (auto c : data) {
80+
if (c == '$' || c == '*') {
81+
boxes.positions.push_back(position);
82+
}
83+
84+
++position.x;
85+
86+
if (position.x == size.width) {
87+
position.x = 0;
88+
++position.y;
89+
}
90+
}
91+
92+
return boxes;
93+
}
94+
95+
96+
enum Block {
97+
Wall,
98+
Goal,
99+
Floor,
100+
};
101+
102+
struct Level {
103+
gf::Array2D<Block, int> blocks;
104+
105+
Level(gf::Vector2i size) : blocks(size) { }
106+
};
107+
108+
Level makeLevel(gf::Span<const char> data, gf::Vector2i size) {
109+
Level level(size);
110+
gf::Vector2i position = gf::vec(0, 0);
111+
112+
for (auto c : data) {
113+
switch (c) {
114+
case ' ':
115+
case '@':
116+
case '$':
117+
level.blocks(position) = Block::Floor;
118+
break;
119+
case '.':
120+
case '+':
121+
case '*':
122+
level.blocks(position) = Block::Goal;
123+
break;
124+
case '#':
125+
level.blocks(position) = Block::Wall;
126+
break;
127+
default:
128+
assert(false);
129+
break;
130+
}
131+
132+
++position.x;
133+
134+
if (position.x == size.width) {
135+
position.x = 0;
136+
++position.y;
137+
}
138+
}
139+
140+
return level;
141+
}
142+
143+
}
144+
145+
146+
int main() {
147+
constexpr gf::Vector2i ScreenSize = gf::vec(800, 600);
148+
gf::Window window("sokoban01", ScreenSize);
149+
gf::RenderWindow renderer(window);
150+
151+
skb::Hero hero = skb::makeHero(skb::LevelData, skb::LevelSize);
152+
skb::Boxes boxes = skb::makeBoxes(skb::LevelData, skb::LevelSize);
153+
skb::Level level = skb::makeLevel(skb::LevelData, skb::LevelSize);
154+
155+
gf::Direction direction = gf::Direction::Center;
156+
157+
renderer.clear(gf::Color::White);
158+
159+
while (window.isOpen()) {
160+
gf::Event event;
161+
162+
while (window.pollEvent(event)) {
163+
switch (event.type) {
164+
case gf::EventType::Closed:
165+
window.close();
166+
break;
167+
168+
case gf::EventType::KeyPressed:
169+
switch (event.key.keycode) {
170+
case gf::Keycode::Up:
171+
direction = gf::Direction::Up;
172+
break;
173+
case gf::Keycode::Right:
174+
direction = gf::Direction::Right;
175+
break;
176+
case gf::Keycode::Down:
177+
direction = gf::Direction::Down;
178+
break;
179+
case gf::Keycode::Left:
180+
direction = gf::Direction::Left;
181+
break;
182+
}
183+
}
184+
}
185+
186+
if (direction != gf::Direction::Center) {
187+
gf::Vector2i target = hero.position + gf::displacement(direction);
188+
189+
if (level.blocks(target) != skb::Block::Wall) {
190+
if (boxes.hasBox(target)) {
191+
gf::Vector2i behind = target + gf::displacement(direction);
192+
193+
if (level.blocks(behind) != skb::Wall && !boxes.hasBox(behind)) {
194+
hero.position = target;
195+
boxes.changeBox(target, behind);
196+
}
197+
} else {
198+
hero.position = target;
199+
}
200+
}
201+
202+
direction = gf::Direction::Center;
203+
}
204+
205+
206+
207+
renderer.clear();
208+
209+
constexpr float BlockSize = 64.0f;
210+
constexpr float BoxSize = BlockSize * 0.8f;
211+
constexpr float HeroRadius = BlockSize * 0.3f;
212+
constexpr float HeroThickness = BlockSize * 0.05f;
213+
214+
gf::Vector2f offset = (ScreenSize - skb::LevelSize * BlockSize) / 2;
215+
gf::Vector2i coordinates;
216+
217+
for (coordinates.y = 0; coordinates.y < skb::LevelSize.height; ++coordinates.y) {
218+
for (coordinates.x = 0; coordinates.x < skb::LevelSize.width; ++coordinates.x) {
219+
gf::RectangleShape block({ BlockSize, BlockSize });
220+
221+
if (level.blocks(coordinates) == skb::Block::Wall) {
222+
block.setColor(gf::Color::Red);
223+
} else {
224+
block.setColor(gf::Color::Gray(0.75f));
225+
}
226+
227+
block.setPosition(coordinates * BlockSize + offset);
228+
renderer.draw(block);
229+
230+
if (level.blocks(coordinates) == skb::Block::Goal) {
231+
gf::RectangleShape goal({ BlockSize / 5, BlockSize / 5 });
232+
goal.setColor(gf::Color::Black);
233+
goal.setPosition(coordinates * BlockSize + offset + BlockSize / 2);
234+
goal.setAnchor(gf::Anchor::Center);
235+
renderer.draw(goal);
236+
}
237+
}
238+
}
239+
240+
for (auto position : boxes.positions) {
241+
gf::RectangleShape box({ BoxSize, BoxSize });
242+
box.setColor(gf::Color::darker(gf::Color::Blue));
243+
box.setPosition(position * BlockSize + offset + BlockSize / 2);
244+
box.setAnchor(gf::Anchor::Center);
245+
renderer.draw(box);
246+
}
247+
248+
gf::CircleShape pusher(HeroRadius);
249+
pusher.setColor(gf::Color::Orange);
250+
pusher.setOutlineColor(gf::Color::darker(gf::Color::Orange));
251+
pusher.setOutlineThickness(HeroThickness);
252+
pusher.setPosition(hero.position * BlockSize + offset + BlockSize / 2);
253+
pusher.setAnchor(gf::Anchor::Center);
254+
renderer.draw(pusher);
255+
256+
renderer.display();
257+
}
258+
259+
}

0 commit comments

Comments
 (0)