#!/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)))))