Add extensive comments to logic.bqn

This commit is contained in:
Rampoina 2023-03-05 17:55:25 +01:00
parent a894380402
commit 37d75d3087
1 changed files with 102 additions and 43 deletions

145
logic.bqn
View File

@ -1,61 +1,123 @@
#!/usr/bin/env BQN #!/usr/bin/env BQN
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
# SPDX-FileCopyrightText: 2023 Rampoina <rampoina@protonmail.com> # SPDX-FileCopyrightText: 2023 Rampoina <rampoina@protonmail.com>
Game{
𝕊 levelPathcharscolors: FindIdx,SplitOnEmpty•Import "utils.bqn"
moves00 # list of moves, each move is a direction, we start without moving
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 floor,player,box,machine,pmachine,wall,lmirror,rmirror,hbeam,vbeam,xbeam,llaser,rlaser,ulaser,dlaserchars
beamshbeamvbeamxbeam
mirrorslmirrorrmirror mirrorslmirrorrmirror # the mirrors to reflect
lasersllaserrlaserulaserdlaser 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 movablesplayerboxmirrors # player, box and mirrors
opaqueplayerboxmachinewalllasers # non laser reflecting
emptiesfloorbeams # floor and laser beams # And *opaque* objects which don't reflect lasers:
opaqueplayerboxmachinewalllasers
# *Empty* objects can contain other ones on top:
emptiesfloorbeams
cols(chars)<(colors) # start with all glyphs being the default color # Colors:
cols (1colors)˙(pmachine) # The parameter 'color' is a list of colors passed to the Game function to alter the
cols (2colors)¨(mirrors) cols(chars)<(colors) # base color,
cols (3colors)¨(beams) cols (1colors)˙(pmachine) # the color for the powered machine
Colorize{𝕩˜cols˜chars𝕩} # 𝕩: character | Turn character into color+character cols (2colors)¨(mirrors) # the color for the mirrors
cols (3colors)¨(beams) # the color for the laser beams
lTiles{𝕩movables ? 𝕩floor; 𝕩}¨chars # list of all the possible tiles as lists of numbers
# Turns the level from ascii strings into a 2d matrix of lists # We use a list of game objects (ints) to represent each tile
Ascii2Matrix{(chars𝕩)lTiles}¨(˝·¬2+) lTiles{𝕩movables ?
𝕩floor; # the movables are on top of the floor (list of 2 elements)
# 𝕨 Tiles 𝕩 | 𝕩: object coordinate (3‿1) | 𝕨: direction vector (¯1‿0) 𝕩 # And the rest are a list of just that element
# result: ⟨ ⟨ 3 1 ⟩ ⟨ 2 1 ⟩ ⟨ 1 1 ⟩ ⟩ }¨chars
# returns 3 tiles in the specified direction from the
Tiles{𝕩,𝕩+𝕨,𝕩+2×𝕨} # given object (including itself) # We transform each character into its tile representation and we transform
# it into a 2d matrix
# Move 𝕩 | 𝕩: ⟨⟨1,0⟩,⟨0⟩⟩ (2 tiles) | result: ⟨⟨0⟩,⟨1,0⟩⟩ # the matrix is padded to ensure that that we can always get 3 tiles
# Move the first object in the first tile to the second tile. # centered on the player and pointing to the current direction
# Only move Movable tile -> Empty tile 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 # 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 # if it is it means that the object was unmovable (next to a wall) so we do nothing
Move{ab:1a,(a)b}{´(movablesempties)<¨𝕩} Move{ab:1a,(a)b}{´(movablesempties)<¨𝕩}
# Push 𝕩 | 𝕩: ⟨⟨1,0⟩,⟨2,0⟩,⟨0,0)⟩ (3 tiles) | result: ⟨⟨0⟩,⟨1,0⟩,⟨2,0)⟩ # 𝕩: ⟨⟨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) # Given 3 tiles try to Push the second tile (possibly a movable object)
# and afterwards try to move the first one (player) if possible # and afterwards try to move the first one (the player) if possible
PushMove(2)Move(1) PushMove(2)Move(1)
FindIdx/() # 𝕩: 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 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 # w‿d Bounce x | x: map | w‿d: w: current position, d: direction of the laser
# Calculates the bounces of a laser beam recursively # Calculates the bounces of a laser beam recursively
Bounce{(wd)S x:{ Bounce{(wd)S x:{
opaque˜wx?(machine=wx)x,pmachine˙(w)x@; # laser hits a non-mirror opaque˜wx? # When the beam touches an opaque object (not a mirror):
empties˜wx ? # Empty space (machine=wx)x, # we do nothing if it doesn't touch a machine
w+d,dS{((×d)hbeamhbeamxbeamxbeam,vbeamxbeamvbeamxbeam)˜floorhbeamvbeamxbeam𝕩}(w)x; # Draw laser and recurse pmachine˙(w)x # and if it does we change it to a powered machine
# laser hits a mirror @;
dd×-¬lmirror=wx # calculate the mirror bounce direction empties˜wx ? # When the beam passes through an empty space:
w+d,dS x } # recurse to the next position # 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 𝕩 | 𝕩: map | calculates the bounces for each laser
Shoot{𝕩 {𝕨Bounce´𝕩} <0¯1,<01,<¯10,<10(˜¨+)¨ FindIdx(¨𝕩)¨ lasers} Shoot{𝕩 {𝕨Bounce´𝕩} <0¯1,<01,<¯10,<10(˜¨+)¨ FindIdx(¨𝕩)¨ lasers}
Step{Push((𝕨 Tiles player FindIdx ¨𝕩))𝕩} # 𝕨 S 𝕩 | 𝕨: direction | 𝕩:level | Step the game
DrawLevel{´¨<˘Colorize¨chars˜+´¨Shoot 𝕨 Step´ 𝕩} # 𝕨 Draw 𝕩 | 𝕨: levels | 𝕩: moves | Draw the game in ASCII # - The Player wins when all machines are powered:
Win{¬´˝machine=¨Shoot 𝕩}# W 𝕩 | 𝕩: level | [W]in condition, no unpowered machines after shooting laser 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<𝕩} Next{movesmoves<𝕩}
Undo{𝕊:moves(-1<)moves} Undo{𝕊:moves(-1<)moves}
Draw{𝕊:•Out¨ (currentLevellevels) DrawLevel moves} Draw{𝕊:•Out¨ (currentLevellevels) DrawLevel moves}
@ -63,7 +125,4 @@ Game⇐{
Reset{𝕊:moves00} Reset{𝕊:moves00}
NextLevel{𝕊:currentLevelcurrentLevel+1Reset @} NextLevel{𝕊:currentLevelcurrentLevel+1Reset @}
Over{𝕊:currentLevel<levels} Over{𝕊:currentLevel<levels}
SplitOnEmpty{𝕩˜(-˜+`׬)0=¨𝕩}
levelsAscii2Matrix¨>¨SplitOnEmpty•FLines levelPath # Load file containing levels
currentLevel0
} }