Skip to content

Commit cc9574b

Browse files
authored
Merge pull request #13 from Dino-Kupinic/6-create-setup-readme-section
feat: small changes
2 parents 83f49ed + 77a0048 commit cc9574b

7 files changed

Lines changed: 100 additions & 100 deletions

File tree

.github/pocoloco.png

284 KB
Loading

README.md

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,47 @@
1-
# Pocoloco Solver in Python with 3D Visualiziation
1+
# Pocoloco Solver
2+
3+
Pocoloco solver solves the [Pocoloco Puzzle](https://rombol.de/products/poco-loco-yavuz-demirhan-turkei-2014-holzspiel-denkspiel-knobelspiel-geduldspiel-aus-holz?srsltid=AfmBOoqXSUB5CWBYS7NhAHXwsx_IGFGyIFNRlFZKRMo88o-vCOwNQh5x) in Python with 3d visualization
4+
5+
![Pocoloco Solver](.github/pocoloco.png)
6+
7+
> [!CAUTION]
8+
> This project only works on Windows
29
310
## 🙌 Installation and Configuration
411

5-
### Installation for Development
6-
1. clone the repository:
12+
1. Clone the repository:
13+
714
```bash
815
git clone https://github.com/Dino-Kupinic/PocolocoSolver.git
16+
cd PocolocoSolver
17+
```
18+
19+
2. Create and activate a Python virtual environment:
20+
21+
```bash
22+
python -m venv venv
23+
source venv/bin/activate
24+
```
25+
26+
3. Install required packages:
27+
28+
```bash
29+
pip install -r requirements.txt
30+
pip install panda3d
31+
```
32+
33+
4. Run the application:
34+
35+
```bash
36+
python src/playground.py
937
```
10-
// WIP
11-
38+
39+
5. Start the renderer:
40+
41+
```bash
42+
python src/visualisation/renderer.py
43+
```
44+
1245
## 😎 Authors
1346

1447
- [@Dino Kupinic](https://www.github.com/Dino-Kupinic)
@@ -25,4 +58,3 @@ Python, Panda3D
2558
## 🦞 License
2659

2760
[MIT](https://choosealicense.com/licenses/mit/)
28-

requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
numpy~=1.26.3
2+
Panda3D~=1.10.14
File renamed without changes.

src/visualisation/renderer.py

Lines changed: 60 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,13 @@ def stepBackward():
6565
def zoomIn():
6666
global cam_r
6767
cam_r = max(2, cam_r - 1)
68-
calcCameraPosition()
68+
calc_camera_position()
6969

7070

7171
def zoomOut():
7272
global cam_r
7373
cam_r += 1
74-
calcCameraPosition()
74+
calc_camera_position()
7575

7676

7777
def adjustSpeed():
@@ -101,56 +101,31 @@ def adjustSpeed():
101101
"lookDown": False
102102
}
103103

104-
# helper function for normalizing vector to length 1
105104
def normalized(*args):
106-
myVec = LVector3(*args)
107-
myVec.normalize()
108-
return myVec
105+
vec = LVector3(*args)
106+
vec.normalize()
107+
return vec
109108

110-
# The purpose is to generate a basic structure for 3D geometry.
111-
def createGeometry():
112-
# A vertex is a data point that defines a point in 3D space. It contains a position (x/y/z) and a color.
113-
114-
# This creates the format for a vertex, with positions and normals.
109+
def create_geometry():
115110
format = GeomVertexFormat.getV3n3()
116-
# Creates a container for vertex data, named piece, using the specified format and a hint "UHStatic",
117-
# indicating that the data won't change frequently.
118111
vdata = GeomVertexData('piece', format, Geom.UHStatic)
119-
120-
# Two instances, vertex and normal, are created.
121-
# They are used to write data into the vdata.
122112
vertex = GeomVertexWriter(vdata, 'vertex')
123113
normal = GeomVertexWriter(vdata, 'normal')
124-
125-
# Is created with the GeomVertexData (vdata) to form a geometry container (scene)
126-
# This geometry can later be added to a node for rendering
127114
scene = Geom(vdata)
128115

129116
return scene, (vertex, normal)
130117

131118

132-
# The purpose is to take a geom object, encapsulate it within a geomnode named scene,
133-
# then attach this geomnode to the rendernode and return a nodepath to the rendered geometry.
134-
# This function is a convenient way to organize/integrate geometry into a Panda3D scene.
135-
def createGeomNode(geom):
136-
# Creates a geomnode named scene
119+
def create_geom_node(geom):
137120
node = GeomNode('scene')
138-
139-
# Unpacks the returned tuple from the geom argument
140121
scene,_ = geom
141-
142-
# Adding the geom object to the geomnode
143122
node.addGeom(scene)
123+
return render.attachNewNode(node)
144124

145-
# Creates a nodepath by attaching the geomnode to the render node
146-
renderedNode = render.attachNewNode(node)
147-
148-
return renderedNode
149-
150-
def makeAABox(pMin, pMax):
151-
geom = createGeometry()
125+
def make_box(pMin, pMax):
126+
geom = create_geometry()
152127
addAABox(pMin, pMax, geom)
153-
return createGeomNode(geom)
128+
return create_geom_node(geom)
154129

155130
# Extends an existing geometry with an axex aligned box
156131
# corner1 and corner 2 must be opposite corners
@@ -194,15 +169,15 @@ def addAABox(corner1, corner2, geom):
194169
scene.addPrimitive(tris)
195170

196171

197-
def addAmbientLight(intensity=0.5):
198-
aLight = AmbientLight('ambientLight')
199-
aLightColor = tuple(3 * [intensity] + [1])
200-
aLight.setColor(aLightColor)
201-
aLightNP = render.attachNewNode(aLight)
202-
render.setLight(aLightNP)
172+
def add_ambient_light(intensity=0.5):
173+
light = AmbientLight('ambientLight')
174+
light_color = tuple(3 * [intensity] + [1])
175+
light.setColor(light_color)
176+
light_np = render.attachNewNode(light)
177+
render.setLight(light_np)
203178

204179

205-
def addPointLight(pos, intensity=1):
180+
def add_point_light(pos, intensity=1):
206181
pLight = PointLight('plight')
207182
pLightColor = tuple(3 * [intensity] + [1])
208183
pLight.setColor(pLightColor)
@@ -224,38 +199,38 @@ def buildPieces(piecePosition, bridgePosition, color):
224199
bridge_y = bridgePosition[1]
225200

226201
#top part
227-
box = makeAABox((-1 + x_offset,-1 + y_offset,-1),(0 + x_offset,0 + y_offset,0))
202+
box = make_box((-1 + x_offset, -1 + y_offset, -1), (0 + x_offset, 0 + y_offset, 0))
228203
box.setColor(*color)
229204
box.reparentTo(blocknp)
230-
box = makeAABox((0 + x_offset,-1 + y_offset,-1),(1 + x_offset,0 + y_offset,0))
205+
box = make_box((0 + x_offset, -1 + y_offset, -1), (1 + x_offset, 0 + y_offset, 0))
231206
box.setColor(*color)
232207
box.reparentTo(blocknp)
233-
box = makeAABox((-1 + x_offset, 0 + y_offset, -1), (0 + x_offset,1 + y_offset,0))
208+
box = make_box((-1 + x_offset, 0 + y_offset, -1), (0 + x_offset, 1 + y_offset, 0))
234209
box.setColor(*color)
235210
box.reparentTo(blocknp)
236-
box = makeAABox((0 + x_offset, 0 + y_offset, -1), (1 + x_offset, 1 + y_offset,0))
211+
box = make_box((0 + x_offset, 0 + y_offset, -1), (1 + x_offset, 1 + y_offset, 0))
237212
box.setColor(*color)
238213
box.reparentTo(blocknp)
239214

240215
#bridge part
241-
box = makeAABox((-1 + x_offset + bridge_x, -1 + y_offset + bridge_y, -2), (0 + x_offset, 0 + y_offset, -1))
216+
box = make_box((-1 + x_offset + bridge_x, -1 + y_offset + bridge_y, -2), (0 + x_offset, 0 + y_offset, -1))
242217
box.setColor(*color)
243218
box.reparentTo(blocknp)
244-
box = makeAABox((-1 + x_offset + bridge_x, -1 + y_offset + bridge_y, -3), (0 + x_offset, 0 + y_offset, -2))
219+
box = make_box((-1 + x_offset + bridge_x, -1 + y_offset + bridge_y, -3), (0 + x_offset, 0 + y_offset, -2))
245220
box.setColor(*color)
246221
box.reparentTo(blocknp)
247222

248223
#bottom part
249-
box = makeAABox((-1 + x_offset, -1 + y_offset, -4), (0 + x_offset, 0 + y_offset, -3))
224+
box = make_box((-1 + x_offset, -1 + y_offset, -4), (0 + x_offset, 0 + y_offset, -3))
250225
box.setColor(*color)
251226
box.reparentTo(blocknp)
252-
box = makeAABox((0 + x_offset, -1 + y_offset, -4), (1 + x_offset, 0 + y_offset, -3))
227+
box = make_box((0 + x_offset, -1 + y_offset, -4), (1 + x_offset, 0 + y_offset, -3))
253228
box.setColor(*color)
254229
box.reparentTo(blocknp)
255-
box = makeAABox((-1 + x_offset, 0 + y_offset, -4), (0 + x_offset, 1 + y_offset, -3))
230+
box = make_box((-1 + x_offset, 0 + y_offset, -4), (0 + x_offset, 1 + y_offset, -3))
256231
box.setColor(*color)
257232
box.reparentTo(blocknp)
258-
box = makeAABox((0 + x_offset, 0 + y_offset, -4), (1 + x_offset, 1 + y_offset, -3))
233+
box = make_box((0 + x_offset, 0 + y_offset, -4), (1 + x_offset, 1 + y_offset, -3))
259234
box.setColor(*color)
260235
box.reparentTo(blocknp)
261236

@@ -285,91 +260,86 @@ def buildBarrierBox(color):
285260
for x in range(-2, 4):
286261
for z in range(-4, 0):
287262
y = -2
288-
box = makeAABox((x, y, z), (x + 1, y + 1, z + 1))
263+
box = make_box((x, y, z), (x + 1, y + 1, z + 1))
289264
box.setColor(*color)
290-
box = makeAABox((x, y + 5, z), (x + 1, y + 6, z + 1))
265+
box = make_box((x, y + 5, z), (x + 1, y + 6, z + 1))
291266
box.setColor(*color)
292267

293268
for y in range(-2, 2):
294269
for z in range(-4, 0):
295270
x = -2
296-
box = makeAABox((x, y + 1, z), (x + 1, y + 2, z + 1))
271+
box = make_box((x, y + 1, z), (x + 1, y + 2, z + 1))
297272
box.setColor(*color)
298-
box = makeAABox((x + 5, y + 1, z), (x + 6, y + 2, z + 1))
273+
box = make_box((x + 5, y + 1, z), (x + 6, y + 2, z + 1))
299274
box.setColor(*color)
300275

301276
for x in range(2):
302277
y = -1
303278
z = -3
304-
box = makeAABox((x, y, z), (x + 1, y + 1, z + 1))
279+
box = make_box((x, y, z), (x + 1, y + 1, z + 1))
305280
box.setColor(*color)
306-
box = makeAABox((x, y + 3, z), (x + 1, y + 4, z + 1))
281+
box = make_box((x, y + 3, z), (x + 1, y + 4, z + 1))
307282
box.setColor(*color)
308283

309-
box = makeAABox((0, -1, -2), (1, 0, -1))
284+
box = make_box((0, -1, -2), (1, 0, -1))
310285
box.setColor(*color)
311-
box = makeAABox((1, 2, -2), (2, 3, -1))
286+
box = make_box((1, 2, -2), (2, 3, -1))
312287
box.setColor(*color)
313288

314-
def startSequence():
289+
def start_sequence():
315290
global sequence_started
316291
if not sequence_started:
317292
cameraWarning.destroy()
318293
sequence.start()
319294
sequence.setPlayRate(0.5)
320295
sequence_started = True
321296

322-
def calcCameraPosition():
297+
def calc_camera_position():
323298
cam_x = cam_r * cos(cam_alpha) * cos(cam_beta)
324299
cam_y = cam_r * sin(cam_alpha) * cos(cam_beta)
325300
cam_z = cam_r * sin(cam_beta)
326301
base.camera.setPos(cam_x, cam_y, cam_z)
327302
base.camera.lookAt(0, 0, 0)
328303

329-
def updateCamera(task):
330-
# ensures movement for horizontal and vertical axes respectively
304+
def update_camera(task):
331305
global cam_alpha, cam_beta
332-
# smooth movement using delta time
333306
dt = globalClock.getDt()
334307

335308
moved = False
336309

337310
if key_states["lookLeft"]:
338311
cam_alpha -= turn_speed * dt
339-
calcCameraPosition()
312+
calc_camera_position()
340313
moved = True
341314
if key_states["lookRight"]:
342315
cam_alpha += turn_speed * dt
343-
calcCameraPosition()
316+
calc_camera_position()
344317
moved = True
345318
if key_states["lookUp"]:
346319
cam_beta += turn_speed * dt
347320
if cam_beta > PI / 2:
348321
cam_beta = PI / 2 - 0.05
349-
calcCameraPosition()
322+
calc_camera_position()
350323
moved = True
351324
if key_states["lookDown"]:
352325
cam_beta -= turn_speed * dt
353326
if cam_beta < -PI / 2:
354327
cam_beta = -PI / 2 + 0.05
355-
calcCameraPosition()
328+
calc_camera_position()
356329
moved = True
357330

358-
# start the sequence if the camera was moved in any direction
359-
360-
# CHECK IF SEQUENCE STARTED GLOBAL FLAG NEEDED
361331
if moved:
362-
startSequence()
332+
start_sequence()
363333

364334
return Task.cont
365335

366-
def pressKey(key):
336+
def press_key(key):
367337
key_states[key] = True
368338

369-
def releaseKey(key):
339+
def release_key(key):
370340
key_states[key] = False
371341

372-
def importPieces():
342+
def import_pieces():
373343
f = open('pieces.json')
374344
data = json.load(f)
375345
f.close()
@@ -394,9 +364,9 @@ def importPieces():
394364
style=1, fg=(1, 1, 1, 1), pos=(0.1, 0.1), scale=.07,
395365
parent=base.a2dBottomLeft, align=TextNode.ALeft)
396366

397-
addAmbientLight()
398-
addPointLight((-3, -4, 2))
399-
calcCameraPosition()
367+
add_ambient_light()
368+
add_point_light((-3, -4, 2))
369+
calc_camera_position()
400370

401371
buildBarrierBox([0.5, 0.5, 0.5, 1])
402372

@@ -405,24 +375,22 @@ def importPieces():
405375
piece3 = buildPieces([2, 0], [2, 0], [0, 1, 0, 1]) # Grün
406376
piece4 = buildPieces([2, 2], [2, 2], [0, 0, 1, 1]) # Blau
407377

408-
piece_sequence = importPieces()
378+
piece_sequence = import_pieces()
409379
piece_mapping = [piece4, piece2, piece3, piece1]
410380
piece_intervals = createIntervalPositions(piece_sequence, piece_mapping)
411381

412382
sequence = Sequence(*piece_intervals)
413383

414-
# accept key events for all 8 directions
415-
base.accept('arrow_left', pressKey, ["lookLeft"])
416-
base.accept('arrow_left-up', releaseKey, ["lookLeft"])
417-
base.accept('arrow_right', pressKey, ["lookRight"])
418-
base.accept('arrow_right-up', releaseKey, ["lookRight"])
419-
base.accept('arrow_up', pressKey, ["lookUp"])
420-
base.accept('arrow_up-up', releaseKey, ["lookUp"])
421-
base.accept('arrow_down', pressKey, ["lookDown"])
422-
base.accept('arrow_down-up', releaseKey, ["lookDown"])
384+
base.accept('arrow_left', press_key, ["lookLeft"])
385+
base.accept('arrow_left-up', release_key, ["lookLeft"])
386+
base.accept('arrow_right', press_key, ["lookRight"])
387+
base.accept('arrow_right-up', release_key, ["lookRight"])
388+
base.accept('arrow_up', press_key, ["lookUp"])
389+
base.accept('arrow_up-up', release_key, ["lookUp"])
390+
base.accept('arrow_down', press_key, ["lookDown"])
391+
base.accept('arrow_down-up', release_key, ["lookDown"])
423392
base.accept('escape', sys.exit)
424393

425-
# runs the application
426394
pauseButton = DirectButton(
427395
text="Pause/Play", scale=0.07, pos=(-0.5, 0.5, -0.4),
428396
command=toggleSequence, parent=base.a2dTopRight
@@ -458,7 +426,5 @@ def importPieces():
458426
align=TextNode.ACenter, parent=base.a2dBottomCenter
459427
)
460428

461-
# starts the camera detection
462-
taskMgr.add(updateCamera, "updateCameraTask")
463-
429+
taskMgr.add(update_camera, "update_camera_task")
464430
base.run()

0 commit comments

Comments
 (0)