#!/usr/bin/env janet
# Scoundrel roguelike game port to Janet
# Created by Luxferre in 2026, released into public domain

# helper method section

# RNG helpers
(def rng (math/rng (os/cryptorand 8)))
(defn randint [max] (math/rng-int rng max))

# Shuffle helper
(defn shuffle [vec]
  (def l (length vec))
  (var j 0)
  (var t 0)
  (for i 1 l
    (set j (randint (+ i 1)))
    (set t (vec i))
    (put vec i (vec j))
    (put vec j t)))

# Read a single-letter choice from the stdin
(defn get-choice [prompt]
  (let [rawc (getline prompt)]
    (if (> (length rawc) 0)
      (string/ascii-lower (string/slice rawc 0 1))
      nil)))

# Ask user for y/n confirmation (with custom prompt)
(defn get-confirm [prompt]
  (var resp nil)
  (var valid false)
  (while (not valid)
    (set resp (get-choice prompt))
    (set valid (string/check-set "yn" resp)))
  (= resp "y"))

# data section

# The Deck
(def deck (array/concat (range 1 14) (range 1 32)))

# The Room
(def room (array/new 4))

# The State
(def state @{
  :score -208
  :hp 20
  :can-run true
  :engaged false
  :drank false
  :weapon 0
  :durability 0
  :rooms-cleared 0
})

# last potion value (for special scoring)
(var last-potion-val 0)

# game logic section

# display the current stats and room
(defn game-field-display []
  (print  "--------------------------")
  (printf "H%02d | W%02d | D%02d | Deck: %02d"
    (state :hp)
    (state :weapon)
    (state :durability)
    (length deck))
  (print  "--------------------------")
  (each x room (cond
    (< x 14) (prinf "M%02d " (+ x 1))
    (< x 23) (prinf "W%02d " (- x 12))
    (prinf "P%02d " (- x 21))))
  (prin "\n 1   2   3   4")
  (if (and (state :can-run) (not (state :engaged))) (prin "    r)un->"))
  (print))

# get valid user's choice
(defn get-valid-choice []
  (var valid false)
  (var ch "")
  (while (not valid)
    (set ch (get-choice "Your choice: "))
    (set valid (cond
        (= ch "r")
          (and (state :can-run) (not (state :engaged)))
        (string/check-set "1234" ch)
          (<= (scan-number ch) (length room))
        false)))
  ch)

# monster handling logic (adds monster value to the score)
(defn handle-monster [val]
  (var healthlost val)
  (printf "You encounter a monster of strength %d." val)
  (if (and (> (state :weapon) 0) (>= (state :durability) val))
    (do # can use weapon
      (if (get-confirm "Use weapon? (y/n) ")
        (do 
          (var weapondiff (- val (state :weapon)))
          (if (< weapondiff 0) (set weapondiff 0))
          (printf "You fight the monster with your %d-level weapon." (state :weapon))
          (set healthlost weapondiff)
          (put state :durability (- val 1))
          (if (< (state :durability) 2) (do
            (put state :weapon 0)
            (put state :durability 0))))
        (print "You fight the monster barehanded.")))
    ( # barehanded encounter
      print "You fight the monster barehanded."
    ))
  (-= (state :hp) healthlost)
  (if (< (state :hp) 0) (put state :hp 0))
  (printf "Lost %d HP. New HP: %d" healthlost (state :hp))
  val)

# weapon handling logic (adds 0 score)
(defn handle-weapon [val]
  (put state :weapon val)
  (put state :durability 14)
  (printf "Equipped a %d-level weapon." val)
  0)

# potion handling logic (adds 0 score)
(defn handle-potion [val]
  (if (state :drank)
    (print "Already drank a potion here! Discarding...")
    (do
      (if (and (= (length room) 1) (= (length deck) 0))
        (set last-potion-val val))
      (+= (state :hp) val)
      (if (> (state :hp) 20) (put state :hp 20))
      (put state :drank true)
      (printf "Drank a %d-level potion. New HP: %d" val (state :hp))))
  0)

# entrypoint

(defn main [& args]
  # print the banner
  (print "----- Scoundrel 2026 -----")
  (print "  Janet port by Luxferre")
  (print "        Good luck!")
  # shuffle the deck
  (shuffle deck)
  # start the main game loop
  (var choice nil)
  (var item nil)
  (var itemidx nil)
  (while (and (> (+ (length deck) (length room)) 0) (> (state :hp) 0))
    (if (not (state :engaged)) # build the room
      (while (and (< (length room) 4) (> (length deck) 0))
        (array/push room (array/pop deck))))
    (game-field-display)
    (set choice (get-valid-choice))
    (if (= choice "r")
      (do # running logic
        (put state :can-run false) # cannot run twice in a row
        (shuffle room) # shuffle this room
        (while (> (length room) 0)
          (array/insert deck 0 (array/pop room)))
        (print "You run from the room.")
      )
      (do # engagement logic
        (put state :engaged true)
        # get the current item from the choice character
        (set itemidx (- (scan-number choice) 1))
        (set item (get room itemidx))
        # remove the element under itemidx from the room
        (array/remove room itemidx)
        # handling routines may or may not modify your score
        (+= (state :score) (cond
          (< item 14) (handle-monster (+ item 1))
          (< item 23) (handle-weapon (- item 12))
          (handle-potion (- item 21))))
        # now, evaluate whether we can move to the next room
        (if (and (= (length room) 1) (> (length deck) 0)) (do
          (put state :can-run true)
          (put state :engaged false)
          (put state :drank false)
          (++ (state :rooms-cleared)))))))

  # now score and output the game result
  (if (> (state :hp) 0)
    (do # win condition
      (put state :score (state :hp))
      (if (= (state :hp) 20)
        (+= (state :score) last-potion-val))
      (prin "You win!"))
    (prin "You lose!"))
  (printf " Your score: %d. Rooms cleared: %d" (state :score) (state :rooms-cleared)))
