Arc/arc.bqn

104 lines
5.6 KiB
BQN
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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 ⟩ #####"
# ┘ ┘
moves00 # list of moves, each move is a direction, we start without moving
chars" λ$⊕λ⭍#/\-|+<>^v" # legal characters
movables127812131415 # player, box mirrors and lasers
opaque123121314156 # non laser reflecting
empties091011 # floor and laser beams
ansi{
e@+27
rede"[31m"
cyane"[36m"
yellowe"[33m"
defaultBe"[0m"
}
SplitOnEmpty{𝕩˜(-˜+`׬)0=¨𝕩}
Ascii2Matrix{(chars𝕩)0,10,20,3,13,23,6,70,80,9,10,11,120,130,140,150}¨(˝·¬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{ab:1a,(a)b}{´(movablesempties)<¨𝕩}
# 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
PushMove(2)Move(1)
FindIdx/()
# 𝕨 Beam 𝕩 | 𝕨: level | 𝕩: position | replace positions with the laser beam
Beam{{"--++"˜" -|+"𝕩}(𝕩)𝕨}
# Recursive Bounce
Bounce{(wd)S x:{
opaque˜wx?(3=wx)x,5˙(w)x@; # laser hits a non-mirror
empties˜wx ? # Empty space
w+d,dS{((×d)991111,10111011)˜091011𝕩}(w)x; # Draw laser and recurse
#⟨w+d,d⟩S{((×⊑d)⊑"--++"‿"|+|+")⊏˜" -|+"⊐𝕩}⌾(w⊸⊑)x; # Draw laser and recurse
# laser hits a mirror
dd×-¬7=wx # calculate the mirror bounce direction
w+d,dS x } # recurse to the next position
}
Shoot{𝕩 {𝕨Bounce´𝕩} <0¯1,<01,<¯10,<10(˜¨+)¨ FindIdx(¨𝕩)¨ 12131415}
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{movesmoves<𝕩}
Undo{𝕊:moves(-1<)moves}
# Main loop
levelsAscii2Matrix¨>¨SplitOnEmpty•FLines "levels" # Load file containing levels
currentLevel0
eansi.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¨ (currentLevellevels) Draw moves
key•term.CharB @
{𝕤Next ("hjkl"=key)/0¯1,10,¯10,01}(key"hjkl")@
{𝕤Next ("aswd"=key)/0¯1,10,¯10,01}(key"aswd")@
{𝕤Undo @}(key='u')@
{𝕤•Out e"[?12l"e"[?25h"•Exit 0}(key='q')@
{𝕤moves00}(key='r')@
{𝕤cleare"[2J"}(((1+>(1+101))¯1+moves)key='u')@
•Out e"8" # Restore cursor position
{𝕤•Out clearansi.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¨ (currentLevellevels) Draw moves
•Out "Moves: "•Repr ¯1+moves
{𝕤•Out "Good job!, press any key to continue to the next level"•term.CharB @e"[H"currentLevelcurrentLevel+1moves00}(Win (currentLevellevels) Step´moves)@
}•_While_{𝕤currentLevel<levels}@
•Out "Well played, you win!"
•Out e"[?12l"e"[?25h"