From 2333c026999b00b7d18996674bec3252ce74d0c1 Mon Sep 17 00:00:00 2001 From: Rampoina Date: Sun, 5 Mar 2023 15:58:49 +0100 Subject: [PATCH] Separate the main loop from the logic --- arc.bqn | 83 ++++++++----------------------------------------------- logic.bqn | 71 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 71 deletions(-) create mode 100644 logic.bqn diff --git a/arc.bqn b/arc.bqn index ee09419..a504a8e 100755 --- a/arc.bqn +++ b/arc.bqn @@ -6,93 +6,34 @@ # The level is a 2d matrix of lists (tiles) # Each list contains the objects of the game represented as numbers ⟨ansi⟩←•Import "ansi.bqn" +⟨Game⟩←•Import "logic.bqn" -moves←⟨0‿0⟩ # list of moves, each move is a direction, we start without moving -chars←" λ$⊕⭍#/\-|+<>^v" # legal characters: -⟨floor,player,box,machine,pmachine,wall,lmirror,rmirror,hbeam,vbeam,xbeam,llaser,rlaser,ulaser,dlaser⟩←↕≠chars -beams←hbeam‿vbeam‿xbeam -mirrors←lmirror‿rmirror -lasers←llaser‿rlaser‿ulaser‿dlaser -movables←player‿box∾mirrors # player, box and mirrors -opaque←player‿box‿machine‿wall∾lasers # non laser reflecting -empties←floor∾beams # floor and laser beams - -colors←(≠chars)⥊ 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←/○⥊⟜(↕≢)∘⍷ -# 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←{(w‿d)S x:{ - ⊑opaque∊˜⊑w⊑x?(machine=⊑w⊑x)◶⟨x,⟨pmachine⟩˙⌾(w⊸⊑)x⟩@; # laser hits a non-mirror - ⊑empties∊˜⊑w⊑x ? # Empty space - ⟨w+d,d⟩S{((×⊑d)⊑⟨hbeam‿hbeam‿xbeam‿xbeam,vbeam‿xbeam‿vbeam‿xbeam⟩)⊏˜floor‿hbeam‿vbeam‿xbeam⊐𝕩}⌾(w⊸⊑)x; # Draw laser and recurse - # laser hits a mirror - d←⌽d×-⊸¬lmirror=⊑w⊑x # calculate the mirror bounce direction - ⟨w+d,d⟩S x } # recurse to the next position -} - -# Shoot 𝕩 | 𝕩: map | calculates the bounces for each laser -Shoot←{𝕩 {𝕨Bounce´⌽𝕩} ∾⟨<0‿¯1,<0‿1,<¯1‿0,<1‿0⟩(⊣⋈˜¨+)¨ FindIdx⟜(⊑¨𝕩)¨ lasers} -Step←{Push⌾((𝕨 Tiles ⊑player FindIdx ⊑¨𝕩)⊸⊑)𝕩} # 𝕨 S 𝕩 | 𝕨: direction | 𝕩:level | Step the game -Draw←{∾´¨<˘Colorize¨chars⊏˜+´¨Shoot 𝕨 Step´ ⌽𝕩} # 𝕨 Draw 𝕩 | 𝕨: levels | 𝕩: moves | Draw the game in ASCII -Win←{¬∨´∨˝machine=⊑¨Shoot 𝕩}# W 𝕩 | 𝕩: level | [W]in condition, no unpowered machines after shooting laser -Next←{moves↩moves∾<𝕩} -Undo←{𝕊:moves↩(-1<≠)⊸↓moves} - -# Main loop -SplitOnEmpty←{𝕩⊔˜(⊢-˜+`׬)0=≠¨𝕩} -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←"" +g←Game "levels" {𝕤 # Loop until the user wins •Out clear∾e∾"[H" # Cursor to origin clear↩"" - •Out "Level: "∾•Repr 1+currentLevel + •Out "Level: "∾•Repr 1+g.currentLevel •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 - •Out "Moves: "∾•Repr ¯1+≠moves - Win (currentLevel⊑levels) Step´⌽moves ? + g.Draw @ + •Out "Moves: "∾•Repr ¯1+≠g.moves + g.WinLevel @ ? •Out "Good job!, press any key to continue to the next level" key←•term.CharB @ clear↩e∾"[2J" - currentLevel↩currentLevel+1⋄moves↩⟨0‿0⟩ + g.NextLevel @ {𝕤⋄•Out e∾"[?12l"∾e∾"[?25h"⋄•Exit 0}⍟(key='q')@ ; key←•term.CharB @ - {𝕤⋄Next ⊑("hjklaswd"=key)/∾˜⟨0‿¯1,1‿0,¯1‿0,0‿1⟩}⍟(⊑key∊"hjklaswd")@ - {𝕤⋄Undo @}⍟(key='u')@ + {𝕤⋄g.Next ⊑("hjklaswd"=key)/∾˜⟨0‿¯1,1‿0,¯1‿0,0‿1⟩}⍟(⊑key∊"hjklaswd")@ + {𝕤⋄g.Undo @}⍟(key='u')@ {𝕤⋄•Out e∾"[?12l"∾e∾"[?25h"⋄•Exit 0}⍟(key='q')@ - {𝕤⋄clear↩e∾"[2J"⋄moves↩⟨0‿0⟩}⍟(key='r')@ - {𝕤⋄clear↩e∾"[2J"}⍟(((1⊸+>○(⌊1+10⋆⁼1⌈⊢)⊢)¯1+≠moves)∧key='u')@ -}•_While_{𝕤⋄currentLevel<≠levels}@ + {𝕤⋄clear↩e∾"[2J"⋄g.Reset @}⍟(key='r')@ + {𝕤⋄clear↩e∾"[2J"}⍟(((1⊸+>○(⌊1+10⋆⁼1⌈⊢)⊢)¯1+≠g.moves)∧key='u')@ +}•_While_{𝕤⋄g.Over @}@ •Out "Well played, you win!" •Out e∾"[?12l"∾e∾"[?25h" diff --git a/logic.bqn b/logic.bqn new file mode 100644 index 0000000..3f7ae1b --- /dev/null +++ b/logic.bqn @@ -0,0 +1,71 @@ +#!/usr/bin/env BQN +# SPDX-License-Identifier: AGPL-3.0-or-later +# SPDX-FileCopyrightText: 2023 Rampoina +⟨ansi⟩←•Import "ansi.bqn" + +Game⇐{ + moves⇐⟨0‿0⟩ # list of moves, each move is a direction, we start without moving + chars←" λ$⊕⭍#/\-|+<>^v" # legal characters: + ⟨floor,player,box,machine,pmachine,wall,lmirror,rmirror,hbeam,vbeam,xbeam,llaser,rlaser,ulaser,dlaser⟩←↕≠chars + beams←hbeam‿vbeam‿xbeam + mirrors←lmirror‿rmirror + lasers←llaser‿rlaser‿ulaser‿dlaser + movables←player‿box∾mirrors # player, box and mirrors + opaque←player‿box‿machine‿wall∾lasers # non laser reflecting + empties←floor∾beams # floor and laser beams + + colors←(≠chars)⥊ 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←/○⥊⟜(↕≢)∘⍷ + # 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←{(w‿d)S x:{ + ⊑opaque∊˜⊑w⊑x?(machine=⊑w⊑x)◶⟨x,⟨pmachine⟩˙⌾(w⊸⊑)x⟩@; # laser hits a non-mirror + ⊑empties∊˜⊑w⊑x ? # Empty space + ⟨w+d,d⟩S{((×⊑d)⊑⟨hbeam‿hbeam‿xbeam‿xbeam,vbeam‿xbeam‿vbeam‿xbeam⟩)⊏˜floor‿hbeam‿vbeam‿xbeam⊐𝕩}⌾(w⊸⊑)x; # Draw laser and recurse + # laser hits a mirror + d←⌽d×-⊸¬lmirror=⊑w⊑x # calculate the mirror bounce direction + ⟨w+d,d⟩S x } # recurse to the next position + } + + # Shoot 𝕩 | 𝕩: map | calculates the bounces for each laser + Shoot←{𝕩 {𝕨Bounce´⌽𝕩} ∾⟨<0‿¯1,<0‿1,<¯1‿0,<1‿0⟩(⊣⋈˜¨+)¨ 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 + Win←{¬∨´∨˝machine=⊑¨Shoot 𝕩}# W 𝕩 | 𝕩: level | [W]in condition, no unpowered machines after shooting laser + Next⇐{moves↩moves∾<𝕩} + Undo⇐{𝕊:moves↩(-1<≠)⊸↓moves} + Draw⇐{𝕊:•Out¨ (currentLevel⊑levels) DrawLevel moves} + WinLevel⇐{𝕊:Win(currentLevel⊑levels)Step´⌽moves} + Reset⇐{𝕊:moves↩⟨0‿0⟩} + NextLevel⇐{𝕊:currentLevel↩currentLevel+1⋄Reset @} + Over⇐{𝕊:currentLevel<≠levels} + SplitOnEmpty←{𝕩⊔˜(⊢-˜+`׬)0=≠¨𝕩} + levels←Ascii2Matrix¨>¨SplitOnEmpty•FLines 𝕩 # Load file containing levels + currentLevel⇐0 +}