aoc-2022/common-lisp/day-7/solution.lisp

116 lines
3.1 KiB
Common Lisp
Executable file

#!/usr/bin/env -S sbcl --script
(require 'asdf)
(require 'cl-ppcre)
(require 'str)
(require 'iterate)
(require 'uiop)
(defpackage :solution
(:use :cl)
(:import-from :iterate :iter)
(:import-from :uiop :if-let)
(:shadow :directory))
(in-package :solution)
(defclass dir ()
((name
:initarg :name
:reader name)
(parent
:initarg :parent
:reader parent)
(files
:initform nil
:accessor files)
(subdirs
:initform nil
:accessor subdirs)))
(defun mkdir (dir dirname)
(push (cons dirname (make-instance 'dir :name dirname :parent dir)) (subdirs dir)))
(defun touch (dir filename filesize)
(push (cons filename filesize) (files dir)))
(defun find-dir (dir dirname)
(if-let ((d (assoc dirname (subdirs dir) :test 'string=)))
(cdr d)
nil))
(defun depth (dir &optional (depth 0))
(if-let ((p (parent dir)))
(depth p (incf depth))
depth))
(defmethod print-object ((obj dir) stream)
(format stream
"~&name: ~a~%parent: ~a~%depth: ~a~%subdirs: ~a~%files: ~a ~&"
(name obj)
(if (> (depth obj) 0)
(name (parent obj)))
(depth obj)
(files obj)
(subdirs obj)))
(defclass fs ()
((root
:initform (make-instance 'dir :name "root" :parent nil)
:reader root)
(cwd
:initform nil
:accessor cwd)))
(defvar fs-indent-factor 4)
(defun cd (fs dirname)
(with-accessors ((cwd cwd)
(root root))
fs
(cond ((string= dirname "/")
(setf cwd root))
((string= dirname "..")
(setf cwd (parent cwd)))
(t
(setf cwd (find-dir cwd dirname))))))
(defun fs-print-tree (dir stream)
(let ((indent (* (depth dir) fs-indent-factor)))
(iter (iter:for (filename . filesize) in (files dir))
(format stream "~vt - ~a size=~a~%" indent filename filesize))
(iter (iter:for (subdirname . subdir) in (subdirs dir))
(format stream "~vt / ~a~%" indent subdirname)
(fs-print-tree subdir stream))))
(defmethod print-object ((obj fs) stream)
(format stream "/ root~%")
(fs-print-tree (root obj) stream))
(defun parse-input (lines)
(iter (iter:for line in lines)
(iter:for parts = (str:split " " line))
(cond ((string= (nth 1 parts) "cd")
(iter:collect (list 'cd (nth 2 parts))))
((string= (nth 0 parts) "dir")
(iter:collect (list 'dir (nth 1 parts))))
((str:digit? (nth 0 parts))
(iter:collect (list 'file (nth 1 parts) (parse-integer (nth 0 parts))))))))
(defun solution-part-1 (events)
(iter (iter:with fs = (make-instance 'fs))
(iter:for event in events)
(let* ((type (car event))
(dir-or-file-name (cadr event))
(size (if (equal type 'file) (car (last event)) nil)))
(with-accessors ((root root)
(cwd cwd))
fs
(cond ((equal type 'cd)
(cd fs dir-or-file-name))
((equal type 'dir)
(mkdir cwd dir-or-file-name))
((equal type 'file)
(touch cwd dir-or-file-name size))))
(iter:finally (return fs)))))