Arc/logic.bqn

129 lines
6.3 KiB
BQN
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>
FindIdx,SplitOnEmpty•Import "utils.bqn"
Game{ # The Game function creates a game object
𝕊 levelPathdcharscharscolors: # from parameters:
# levelPath: the path of the file containing the levels
# dchars: the characters to use for drawing
# colors: a list with colors
# Game representation:
# -------------------------------------------------------------------------------
# The game has the following objects represented as consecutive integers:
floor,player,box,machine,pmachine,wall,lmirror,rmirror,hbeam,vbeam,xbeam,llaser,rlaser,ulaser,dlaserchars
mirrorslmirrorrmirror # the mirrors to reflect
beamshbeamvbeamxbeam # the laser beams
lasersllaserrlaserulaserdlaser # shot by the laser
# Each with every possible orientation
# There are *movable* objects like the following:
movablesplayerboxmirrors # player, box and mirrors
# And *opaque* objects which don't reflect lasers:
opaqueplayerboxmachinewalllasers
# *Empty* objects can contain other ones on top:
emptiesfloorbeams
# Colors:
# The parameter 'color' is a list of colors passed to the Game function to alter the
cols(chars)<(colors) # base color,
cols (1colors)˙(pmachine) # the color for the powered machine
cols (2colors)¨(mirrors) # the color for the mirrors
cols (3colors)¨(beams) # the color for the laser beams
# We use a list of game objects (ints) to represent each tile
lTiles{𝕩movables ?
𝕩floor; # the movables are on top of the floor (list of 2 elements)
𝕩 # And the rest are a list of just that element
}¨chars
# We transform each character into its tile representation and we transform
# it into a 2d matrix
# the matrix is padded to ensure that that we can always get 3 tiles
# centered on the player and pointing to the current direction
Ascii2Matrix{(chars𝕩)lTiles}¨(˝·¬2+) # 𝕩 : Matrix of characters
# Rules:
# -------------------------------------------------------------------------------
#
# - The player can push but not pull any movable object one tile away in the
# current direction to an empty tile:
# 𝕩: ⟨⟨1,0⟩,⟨0⟩⟩ (2 tiles) | result: ⟨⟨0⟩,⟨1,0⟩⟩
# (Try to) 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)<¨𝕩}
# 𝕩: ⟨⟨1,0⟩,⟨2,0⟩,⟨0,0)⟩ (3 tiles) | result: ⟨⟨0⟩,⟨1,0⟩,⟨2,0)⟩
# Given 3 tiles try to Push the second tile (possibly a movable object)
# and afterwards try to move the first one (the player) if possible
PushMove(2)Move(1)
# 𝕩: 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)
# We (try to) push the tile in front of the player
Step{Push((𝕨 Tiles player FindIdx ¨𝕩))𝕩} # 𝕨 S 𝕩 | 𝕨: direction | 𝕩:level | Step the game
# - The lasers shoot lasers beams that get intercepted by opaque objects and
# bounce on mirrors:
# 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? # When the beam touches an opaque object (not a mirror):
(machine=wx)x, # we do nothing if it doesn't touch a machine
pmachine˙(w)x # and if it does we change it to a powered machine
@;
empties˜wx ? # When the beam passes through an empty space:
# we draw the laser beam and recurse to the next:
w+d,dS{ # we choose the type of laser beam to draw
cTile(floorhbeamvbeamxbeam𝕩) # depending on the current tile:
# floor hbeam vbeam xbeam | floor hbeam vbeam xbeam
cBeam ((×d) hbeamhbeamxbeamxbeam , vbeamxbeamvbeamxbeam)
# and beam direction Horizontal | Vertical
cTilecBeam
}(w)x;
# When the beam touches a mirror:
dd×-¬lmirror=wx # calculate the mirror bounce direction
w+d,dS x # and recurse to the next position
}
}
# Shoot 𝕩 | 𝕩: map | calculates the bounces for each laser
Shoot{𝕩 {𝕨Bounce´𝕩} <0¯1,<01,<¯10,<10(˜¨+)¨ FindIdx(¨𝕩)¨ lasers}
# - The Player wins when all machines are powered:
Win{¬´˝machine=¨Shoot 𝕩}# 𝕩: level | Win condition, no unpowered machines after shooting laser
# Drawing:
# -------------------------------------------------------------------------------
Colorize{𝕩˜cols˜dchars𝕩} # 𝕩: character | Turn character into color+character
DrawLevel{´¨<˘Colorize¨dchars˜+´¨Shoot 𝕨 Step´ 𝕩} # 𝕨 Draw 𝕩 | 𝕨: levels | 𝕩: moves | Draw the game in ASCII
# State and state mutating functions:
# -------------------------------------------------------------------------------
moves00 # list of moves, each move is a direction, we start without moving
currentLevel0
levelsAscii2Matrix¨>¨SplitOnEmpty•FLines levelPath # Load file containing levels
Next{movesmoves<𝕩}
Undo{𝕊:moves(-1<)moves}
Draw{𝕊:•Out¨ (currentLevellevels) DrawLevel moves}
WinLevel{𝕊:Win(currentLevellevels)Step´moves}
Reset{𝕊:moves00}
NextLevel{𝕊:currentLevelcurrentLevel+1Reset @}
Over{𝕊:currentLevel<levels} # has the user beaten all of the levels?
}