Celeste, Speed-running and Org Tables

Although I've played Celeste for long time, I'm not the best player. I've finished all the A-Sides minus Farewell, grabbed all the berries and cleared 4 B-sides. Celeste is a game that teaches us to embrace our mistakes as a learning opportunity. That practice makes us better. Not talent, but practice. As such it is a game dear to my heart. It is the second game I introduced to my daughter. While playing with my daughter recently, I've decided that I'm going to take chance at speed-running.

The first game I speed-run was hollow knight, aiming for a under 4 hours time. And although I did improved significantly. Even managed to implement a reliably do a couple of skips my attempts met a wall in the Watcher Knights. Although I managed to reach them in ~1H reliably I never managed to get close to beating them. Even with the help of Fireb0rn.

I'm under no delusions of grandeur, I can't commit the amount of time necessary to be a competitive speed-runner. But I don't need to. I only need to compete with my past self. My goal is a <1 hour Any%.

The baseline

This are currently my personal best times. As you can see I have a lot of room for improvement. Particularly on The Summit and Reflection. I think just not dying would help me bring the times down significantly. My plan is to start practising individual rooms in the summit.

| Level            | Personal Best | 1H Target Times |
|------------------+---------------+-----------------|
| Forsaken City    |      2:00.717 |            1:47 |
| Old Site         |      5:00.492 |            2:42 |
| Celestial Resort |     16:03.373 |            8:00 |
| Golden Ridge     |     17:15.479 |            4:55 |
| Mirror Temple    |     19:15.507 |            7:51 |
| Reflection       |     29:00.477 |            8:20 |
| The Summit       |     40:52.063 |           16:00 |
|------------------+---------------+-----------------|
| Total            |   2:09:28.108 |       49:35.000 |
#+TBLFM: @9$2='(celeste-ms->timestamp (apply '+ (mapcar 'celeste-timestamp->ms '(@2..@8))))::@9$3='(celeste-ms->timestamp (apply '+ (mapcar 'celeste-timestamp->ms '(@2..@8))))

Appendix

The code to support the org-mode table formulas, you can place them in your user-lisp-directory.

;;;###autoload
(defun celeste-timestamp->ms (timestamp)
  "Return the number of milliseconds in a celeste timestamp.

A celeste Timestamp has a format of [H:]MM:SS.XXX"
  (let* ((ts timestamp)
         (pivot (cl-position ?. ts :from-end t))
         minutes seconds  milliseconds)
    (if pivot
        (setq milliseconds (cl-parse-integer (seq-subseq ts (1+ pivot))))
      (setq milliseconds 0))
    (setq ts (seq-subseq ts 0 pivot))
    (setq pivot (cl-position ?: ts :from-end t))
    (setq seconds (cl-parse-integer (seq-subseq ts (1+ pivot))))
    (setq ts (seq-subseq ts 0 pivot))
    ;; TODO: Handle hours
    (setq minutes (cl-parse-integer ts))
    (+ (* minutes 60 1000) (* seconds 1000) milliseconds)))

;;;###autoload
(defun celeste-ms->timestamp (milliseconds)
  "Return the timestamp in Celeste's format."
  (let* ((ms milliseconds)
         (hours (/ ms (* 60 60 1000)))
         minutes seconds)
    (if (zerop hours)
        (setq hours nil)
      (setq ms (- ms (* hours (* 60 60 1000)))))
    (setq minutes (/ ms (* 60 1000)))
    (setq ms (- ms (* minutes 60 1000)))
    (setq seconds (/ ms 1000))
    (setq ms (- ms (* seconds 1000)))
    (if hours
        (format "%d:%02d:%02d.%03d" hours minutes seconds ms)
      (format "%d:%02d.%03d" minutes seconds ms))))

(cl-assert (equal (celeste-timestamp->ms "1:47") (+ (* 60 1000)
                                                    (* 47 1000))))
(cl-assert (equal (celeste-timestamp->ms "40:52.063") 2452063))
(cl-assert (equal (celeste-ms->timestamp 2452063) "40:52.063"))
(cl-assert (equal (celeste-ms->timestamp 7768108) "2:09:28.108"))