<commit_before>;; Sokoban, ported from github.com/darius/sturm.

;; In this game you live in a 2-d grid world containing crates, target
;; squares, and impassable walls. You need to push all the crates onto
;; targets. You can only push one crate at once. These simple rules
;; yield an elegant game with scope for a great variety of puzzles,
;; from easy to AI-complete.

;; A square with     is this symbol     or this symbol, when at a target
;; -------------     --------------     --------------------------------
;; Yourself          i                  I
;; A crate           o                  @
;; Nothing                              .
;; A wall            #                  N/A (never a target)

;; Other console Sokobans display the game a little differently: with
;; different symbols and a squeezed aspect ratio. I insert spaces
;; between squares to make them more nearly, y'know, square.

;; Some other Sokoban implementations you might enjoy:
;; http://eloquentjavascript.net/chapter13.html (by <NAME>)
;; http://aurelio.net/projects/sedsokoban/ (by <NAME>)
;; http://code.google.com/p/cleese/source/browse/trunk/experimental/necco/kernel/soko.py
;; (runs without a regular OS, by <NAME>)

(import (use 'sturm)
  cbreak-mode
  get-key render
  green bold unstyled)

(to (main args)
  (let filename
    (be args
      (`(,_) "eg/games/microban")
      (`(,_ ,fname) fname)
      (_ (error ("Usage: ~d [filename]" .format (args 0))))))
  (start @(with-input-file read-collection filename)))

(to (read-collection source)
  (let name source.read-line)
  (let levels-str source.read-all)
  (let grids (for each ((floor-plan (levels-str .split "\n\n")))
               (sokoban-grid<- (parse floor-plan))))
  `(,grids ,name))

(to (start grids name)
  (for cbreak-mode ()
    (play grids name 0)))

(let directions (map<- `((#\h  left) (#\j up) (#\k  down) (#\l   right)
                         (left left) (up  up) (down down) (right right))))

;; The UI to a sequence of Sokoban levels.
(to (play grids name level)
  (let trails (array<-list (each flexarray<- grids)))

  (let heading
    "Move with the arrow keys or HJKL. U to undo.
N/P for next/previous level; Q to quit.

Level ~w ~d Move ~w")

  (begin playing ((level level))
    (let trail (trails level))
    (let grid  trail.last)

    (to (view-grid)
      (for each ((ch grid.unparse.values))
        (let c1 (if ("iI" .find? ch) green unstyled))
        (let c2 (if (".I@" .find? ch) (compose bold c1) c1))
        (c2 ch)))

    (render `(,(heading .format (+ level 1) (name .center 50) trail.count)
              "\n\n"
              ,(view-grid)
              ,@(if grid.won? '("\n\nDone!") '())))

    (be get-key.lowercase
      (#\q  'done)
      (#\n  (playing (level.+ .modulo trails.count)))
      (#\p  (playing (level.- .modulo trails.count)))
      (#\u
       (when (< 1 trail.count)
         trail.pop!)
       (playing level))
      (key
       (when (directions .maps? key)
         (let after grid.copy)
         (after .push (directions key))
         (unless (= grid.unparse after.unparse) ;XXX clumsy
           (trail .push! after)))<commit_msg>tweak<commit_after>;; Sokoban, ported from github.com/darius/sturm.

;; In this game you live in a 2-d grid world containing crates, target
;; squares, and impassable walls. You need to push all the crates onto
;; targets. You can only push one crate at once. These simple rules
;; yield an elegant game with scope for a great variety of puzzles,
;; from easy to AI-complete.

;; A square with     is this symbol     or this symbol, when at a target
;; -------------     --------------     --------------------------------
;; Yourself          i                  I
;; A crate           o                  @
;; Nothing                              .
;; A wall            #                  N/A (never a target)

;; Other console Sokobans display the game a little differently: with
;; different symbols and a squeezed aspect ratio. I insert spaces
;; between squares to make them more nearly, y'know, square.

;; Some other Sokoban implementations you might enjoy:
;; http://eloquentjavascript.net/chapter13.html (by <NAME>)
;; http://aurelio.net/projects/sedsokoban/ (by <NAME>)
;; http://code.google.com/p/cleese/source/browse/trunk/experimental/necco/kernel/soko.py
;; (runs without a regular OS, by <NAME>)

(import (use 'sturm)
  cbreak-mode
  get-key render
  green bold unstyled)

(to (main args)
  (let filename
    (be args
      (`(,_) "eg/games/microban")
      (`(,_ ,fname) fname)
      (_ (error ("Usage: ~d [filename]" .format (args 0))))))
  (start @(with-input-file read-collection filename)))

(to (read-collection source)
  (let name source.read-line)
  (let levels-str source.read-all)
  (let grids (for each ((floor-plan (levels-str .split "\n\n")))
               (sokoban-grid<- (parse floor-plan))))
  `(,grids ,name))

(to (start grids name)
  (for cbreak-mode ()
    (play grids name 0)))

(let directions (map<- `((#\h  left) (#\j up) (#\k  down) (#\l   right)
                         (left left) (up  up) (down down) (right right))))

;; The UI to a sequence of Sokoban levels.
(to (play grids name level)
  (let trails (array<-list (each flexarray<- grids)))

  (let heading
    "Move with the arrow keys or HJKL. U to undo.
N/P for next/previous level; Q to quit.

Level ~w ~d Move ~w")

  (begin playing ((level level))
    (let trail (trails level))
    (let grid  trail.last)

    (to (view-grid)
      (for each ((ch grid.unparse.values))
        (let c1 (if ("iI" .find? ch) green unstyled))
        (let c2 (if (".I@" .find? ch) (compose bold c1) c1))
        (c2 ch)))

    (render `(,(heading .format level.+ (name .center 50) trail.count)
              "\n\n"
              ,(view-grid)
              ,@(if grid.won? '("\n\nDone!") '())))

    (be get-key.lowercase
      (#\q  'done)
      (#\n  (playing (level.+ .modulo trails.count)))
      (#\p  (playing (level.- .modulo trails.count)))
      (#\u
       (when (< 1 trail.count)
         trail.pop!)
       (playing level))
      (key
       (when (directions .maps? key)
         (let after grid.copy)
         (after .push (directions key))
         (unless (= grid.unparse after.unparse) ;XXX clumsy
           (trail .push! after)))
{'fraction_non_alphanumeric': 0.1617787695250239, 'fraction_numerical': 0.004303474657315907, 'mean_word_length': 2.1659939455095865, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1736457', 'n_tokens_mistral': 2106, 'n_tokens_neox': 1991, 'n_words': 698}