First commit
This commit is contained in:
commit
bfd5cd52a2
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "CBQN"]
|
||||||
|
path = CBQN
|
||||||
|
url = https://github.com/dzaima/CBQN.git
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 05c1270344908e98c9f2d06b3671c3646f8634c3
|
|
@ -0,0 +1,12 @@
|
||||||
|
.PHONY: check-and-reinit-submodules all
|
||||||
|
all: .check-and-reinit-submodules
|
||||||
|
@${MAKE} -C CBQN/ MAKEFLAGS=
|
||||||
|
|
||||||
|
run: arc.bqn
|
||||||
|
./CBQN/BQN arc.bqn
|
||||||
|
|
||||||
|
.check-and-reinit-submodules:
|
||||||
|
@if git submodule status | egrep -q '^[-]|^[+]' ; then \
|
||||||
|
echo "INFO: reinitializing git submodules"; \
|
||||||
|
git submodule update --init; \
|
||||||
|
fi
|
|
@ -0,0 +1,25 @@
|
||||||
|
# Arc, a puzzle game about directing laser beams with mirrors
|
||||||
|
|
||||||
|
## Controls
|
||||||
|
Vim style movement keys or WASD keys
|
||||||
|
|
||||||
|
- a/h: left
|
||||||
|
- s/j: down
|
||||||
|
- w/k: up
|
||||||
|
- d/l: right
|
||||||
|
- u: undo
|
||||||
|
- r: restart
|
||||||
|
- q: quit
|
||||||
|
|
||||||
|
## Running
|
||||||
|
|
||||||
|
If you already have BQN and you have it on your path just run `./soko.bqn`
|
||||||
|
|
||||||
|
Otherwise:
|
||||||
|
|
||||||
|
1. `make` to compile CBQN (`make CC=cc` if clang isn't installed)
|
||||||
|
2. `make run` to run the game
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Sokobqn is licensed as AGPLv3
|
|
@ -0,0 +1,103 @@
|
||||||
|
#!/usr/bin/env BQN
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
# SPDX-FileCopyrightText: 2023 Rampoina <rampoina@protonmail.com>
|
||||||
|
#
|
||||||
|
# Arc
|
||||||
|
# The level is a 2d matrix of lists (tiles)
|
||||||
|
# Each list contains the objects of the game:
|
||||||
|
# 0: floor, 1: player, 2: box, 3: goal, 4: wall
|
||||||
|
# 4: player on goal, 5: box on goal
|
||||||
|
#
|
||||||
|
# Example: ASCII:
|
||||||
|
# ┌─ ┌─
|
||||||
|
# ╵ ⟨ 4 ⟩ ⟨ 4 ⟩ ⟨ 4 ⟩ ⟨ 4 ⟩ ⟨ 4 ⟩ ╵"#####
|
||||||
|
# ⟨ 4 ⟩ ⟨ 1 0 ⟩ ⟨ 2 0 ⟩ ⟨ 0 ⟩ ⟨ 4 ⟩ #@*.#
|
||||||
|
# ⟨ 4 ⟩ ⟨ 4 ⟩ ⟨ 4 ⟩ ⟨ 4 ⟩ ⟨ 4 ⟩ #####"
|
||||||
|
# ┘ ┘
|
||||||
|
moves←⟨0‿0⟩ # list of moves, each move is a direction, we start without moving
|
||||||
|
chars←" λ$⊕λ⭍#/\-|+<>^v" # legal characters
|
||||||
|
movables←1‿2‿7‿8‿12‿13‿14‿15 # player, box mirrors and lasers
|
||||||
|
opaque←1‿2‿3‿12‿13‿14‿15‿6 # non laser reflecting
|
||||||
|
empties←0‿9‿10‿11 # floor and laser beams
|
||||||
|
ansi←{
|
||||||
|
e⇐@+27
|
||||||
|
red⇐e∾"[31m"
|
||||||
|
cyan⇐e∾"[36m"
|
||||||
|
yellow⇐e∾"[33m"
|
||||||
|
defaultB⇐e∾"[0m"
|
||||||
|
}
|
||||||
|
|
||||||
|
SplitOnEmpty←{𝕩⊔˜(⊢-˜+`׬)0=≠¨𝕩}
|
||||||
|
Ascii2Matrix←{(⊑chars⊐𝕩)⊑⟨≍0,1‿0,2‿0,≍3,1‿3,2‿3,≍6,7‿0,8‿0,≍9,≍10,≍11,12‿0,13‿0,14‿0,15‿0⟩}¨(⊢↑˝·≍⟜¬2+≢)
|
||||||
|
|
||||||
|
# 𝕨 Tiles 𝕩 | 𝕩: object coordinate (3‿1) | 𝕨: direction vector (¯1‿0)
|
||||||
|
# result: ⟨ ⟨ 3 1 ⟩ ⟨ 2 1 ⟩ ⟨ 1 1 ⟩ ⟩
|
||||||
|
# returns 3 tiles in the specified direction from the
|
||||||
|
Tiles←{⟨𝕩,𝕩+𝕨,𝕩+2×𝕨⟩} # given object (including itself)
|
||||||
|
|
||||||
|
# Move 𝕩 | 𝕩: ⟨⟨1,0⟩,⟨0⟩⟩ (2 tiles) | result: ⟨⟨0⟩,⟨1,0⟩⟩
|
||||||
|
# Move the first object in the first tile to the second tile.
|
||||||
|
# Only move Movable tile -> Empty tile
|
||||||
|
# the second tile can't be a movable object because we moved it previously
|
||||||
|
# if it is it means that the object was unmovable (next to a wall) so we do nothing
|
||||||
|
Move←{a‿b:⟨1↓a,(⊑a)∾b⟩}⍟{∨´(⥊movables≍⌜empties)≡⌜<⊑¨𝕩}
|
||||||
|
|
||||||
|
# Push 𝕩 | 𝕩: ⟨⟨1,0⟩,⟨2,0⟩,⟨0,0)⟩ (3 tiles) | result: ⟨⟨0⟩,⟨1,0⟩,⟨2,0)⟩
|
||||||
|
# Given 3 tiles try to [P]ush the second tile (possible box)
|
||||||
|
# and afterwards try to move the first one (player) if possible
|
||||||
|
Push←Move⌾(2↑⊢)Move⌾(1↓⊢)
|
||||||
|
|
||||||
|
FindIdx←/○⥊⟜(↕≢)∘⍷
|
||||||
|
|
||||||
|
# 𝕨 Beam 𝕩 | 𝕨: level | 𝕩: position | replace positions with the laser beam
|
||||||
|
Beam←{{"--++"⊏˜" -|+"⊐𝕩}⌾(𝕩⊸⊑)𝕨}
|
||||||
|
|
||||||
|
# Recursive Bounce
|
||||||
|
Bounce←{(w‿d)S x:{
|
||||||
|
⊑opaque∊˜⊑w⊑x?(3=⊑w⊑x)◶⟨x,⟨5⟩˙⌾(w⊸⊑)x⟩@; # laser hits a non-mirror
|
||||||
|
⊑empties∊˜⊑w⊑x ? # Empty space
|
||||||
|
⟨w+d,d⟩S{((×⊑d)⊑⟨9‿9‿11‿11,10‿11‿10‿11⟩)⊏˜0‿9‿10‿11⊐𝕩}⌾(w⊸⊑)x; # Draw laser and recurse
|
||||||
|
#⟨w+d,d⟩S{((×⊑d)⊑"--++"‿"|+|+")⊏˜" -|+"⊐𝕩}⌾(w⊸⊑)x; # Draw laser and recurse
|
||||||
|
# laser hits a mirror
|
||||||
|
d←⌽d×-⊸¬7=⊑w⊑x # calculate the mirror bounce direction
|
||||||
|
⟨w+d,d⟩S x } # recurse to the next position
|
||||||
|
}
|
||||||
|
|
||||||
|
Shoot←{𝕩 {𝕨Bounce´⌽𝕩} ∾⟨<0‿¯1,<0‿1,<¯1‿0,<1‿0⟩(⊣⋈˜¨+)¨ FindIdx⟜(⊑¨𝕩)¨ 12‿13‿14‿15}
|
||||||
|
Step←{Push⌾((𝕨 Tiles ⊑1 FindIdx ⊑¨𝕩)⊸⊑)𝕩} # 𝕨 S 𝕩 | 𝕨: direction | 𝕩:level | Step the game
|
||||||
|
Draw←{∾´¨<˘{𝕊'⭍':ansi.yellow∾𝕩;⊑𝕩∊"|-+"?ansi.red∾𝕩;⊑𝕩∊"\/"?ansi.cyan∾𝕩;ansi.defaultB∾𝕩}¨chars⊏˜+´¨Shoot 𝕨 Step´ ⌽𝕩} # 𝕨 Draw 𝕩 | 𝕨: levels | 𝕩: moves | Draw the game in ASCII
|
||||||
|
Win←{¬∨´∨˝3=⊑¨Shoot 𝕩}# W 𝕩 | 𝕩: level | [W]in condition, no unpowered goals after shooting laser
|
||||||
|
Next←{moves↩moves∾<𝕩}
|
||||||
|
Undo←{𝕊:moves↩(-1<≠)⊸↓moves}
|
||||||
|
|
||||||
|
# Main loop
|
||||||
|
levels←Ascii2Matrix¨>¨SplitOnEmpty•FLines "levels" # Load file containing levels
|
||||||
|
currentLevel←0
|
||||||
|
e←ansi.e
|
||||||
|
•term.RawMode 1 # set terminal to raw mode
|
||||||
|
•Out e∾"[?25l"∾e∾"[2J"∾e∾"[H" # Cursor to origin, hide it and clear screen
|
||||||
|
clear←""
|
||||||
|
{𝕤 # Loop until the user wins
|
||||||
|
•Out e∾"[H" # Cursor to origin
|
||||||
|
•Out "Level: "∾•Repr 1+currentLevel
|
||||||
|
•Out e∾"7" # Save cursor position
|
||||||
|
•Out ansi.yellow∾"⭍"∾ansi.defaultB∾" Power the machines (⊕) by moving the mirrors ("∾ansi.cyan∾"\/"∾ansi.defaultB∾") "
|
||||||
|
•Out "Controls: (hjkl or wasd) to move, u to undo, r to reset level, q to quit"
|
||||||
|
•Out¨ (currentLevel⊑levels) Draw moves
|
||||||
|
key←•term.CharB @
|
||||||
|
{𝕤⋄Next ⊑("hjkl"=key)/⟨0‿¯1,1‿0,¯1‿0,0‿1⟩}⍟(⊑key∊"hjkl")@
|
||||||
|
{𝕤⋄Next ⊑("aswd"=key)/⟨0‿¯1,1‿0,¯1‿0,0‿1⟩}⍟(⊑key∊"aswd")@
|
||||||
|
{𝕤⋄Undo @}⍟(key='u')@
|
||||||
|
{𝕤⋄•Out e∾"[?12l"∾e∾"[?25h"⋄•Exit 0}⍟(key='q')@
|
||||||
|
{𝕤⋄moves↩⟨0‿0⟩}⍟(key='r')@
|
||||||
|
{𝕤⋄clear↩e∾"[2J"}⍟(((1⊸+>○(⌊1+10⋆⁼1⌈⊢)⊢)¯1+≠moves)∧key='u')@
|
||||||
|
•Out e∾"8" # Restore cursor position
|
||||||
|
|
||||||
|
{𝕤⋄•Out clear∾ansi.yellow∾"⭍"∾ansi.defaultB∾" Power the machines (⊕) by moving the mirrors ("∾ansi.cyan∾"\/"∾ansi.defaultB∾") "∾(@+10)∾"Controls: (hjkl or wasd) to move, u to undo, r to reset level, q to quit"}⍟(⊑key∊"wasdhjkluqr")@
|
||||||
|
{𝕤⋄•Out "Invalid key: (hjkl or wasd) to move, u to undo, r to reset level"}⍟(¬⊑key∊"wasdhjkluqr")@
|
||||||
|
•Out¨ (currentLevel⊑levels) Draw moves
|
||||||
|
•Out "Moves: "∾•Repr ¯1+≠moves
|
||||||
|
{𝕤⋄•Out "Good job!, press any key to continue to the next level"⋄•term.CharB @⋄e∾"[H"⋄currentLevel↩currentLevel+1⋄moves↩⟨0‿0⟩}⍟(Win (currentLevel⊑levels) Step´⌽moves)@
|
||||||
|
}•_While_{𝕤⋄currentLevel<≠levels}@
|
||||||
|
•Out "Well played, you win!"
|
||||||
|
•Out e∾"[?12l"∾e∾"[?25h"
|
|
@ -0,0 +1,46 @@
|
||||||
|
##############
|
||||||
|
# #
|
||||||
|
# ######⊕ #
|
||||||
|
# #
|
||||||
|
# #
|
||||||
|
# > / #
|
||||||
|
# # λ #
|
||||||
|
##############
|
||||||
|
|
||||||
|
###############
|
||||||
|
# # ⊕ #
|
||||||
|
# v / #### #
|
||||||
|
# #/ # \ #
|
||||||
|
# # ## #
|
||||||
|
# #\ λ / #
|
||||||
|
# #### ## / #
|
||||||
|
# \ #
|
||||||
|
###############
|
||||||
|
|
||||||
|
##############
|
||||||
|
# #
|
||||||
|
# ⊕ #
|
||||||
|
# > λ #
|
||||||
|
# ⊕ #
|
||||||
|
# > / #
|
||||||
|
# #
|
||||||
|
##############
|
||||||
|
|
||||||
|
##############
|
||||||
|
# #
|
||||||
|
# > / #
|
||||||
|
# λ #
|
||||||
|
# #
|
||||||
|
# ⊕ \ #
|
||||||
|
# #
|
||||||
|
##############
|
||||||
|
|
||||||
|
###############
|
||||||
|
# ⊕ #
|
||||||
|
# v / # \#
|
||||||
|
# / # \ #
|
||||||
|
# # /#
|
||||||
|
# #\ / #
|
||||||
|
# # λ # / #
|
||||||
|
# \ #
|
||||||
|
###############
|
Loading…
Reference in New Issue