Arc/arc.bqn

104 lines
5.5 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 represented as ints
moves00 # list of moves, each move is a direction, we start without moving
chars" λ$⊕λ⭍#/\-|+<>^v" # legal characters
movables1278 # player, box and mirrors
opaque123121314156 # non laser reflecting
empties091011 # floor and laser beams
ansi{
e@+27
rede"[31m"
cyane"[36m"
yellowe"[33m"
defaultBe"[0m"
}
# Turns the level from ascii strings into a 2d matrix of lists
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{{"--++"˜" -|+"𝕩}(𝕩)𝕨}
# w‿d Bounce x | x: map | w‿d: w: current position, d: direction of the laser
# Calculates the bounces of a laser beam recursively
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 𝕩 | 𝕩: map | calculates the bounces for each laser
Shoot{𝕩 {𝕨Bounce´𝕩} <0¯1,<01,<¯10,<10(˜¨+)¨ FindIdx(¨𝕩)¨ 12131415}
Step{Push((𝕨 Tiles 1 FindIdx ¨𝕩))𝕩} # 𝕨 S 𝕩 | 𝕨: direction | 𝕩:level | Step the game
Colorize{𝕊'⭍':ansi.yellow𝕩;𝕩"|-+"?ansi.red𝕩;𝕩"\/"?ansi.cyan𝕩;ansi.defaultB𝕩}
Draw{´¨<˘Colorize¨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
SplitOnEmpty{𝕩˜(-˜+`׬)0=¨𝕩}
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 @
•Out e"[H"e"[0J"
currentLevelcurrentLevel+1moves00
}(Win (currentLevellevels) Step´moves)@
}•_While_{𝕤currentLevel<levels}@
•Out "Well played, you win!"
•Out e"[?12l"e"[?25h"