You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

504 lines
8.9 KiB
Plaintext

(;
; Advent of Code 2022: Day 3, in wasm
; by Ashelyn Dawn
;)
(module
(memory (export "memory") 10)
(global $readLoc (mut i32) (i32.const 0))
(global $inputEnd (mut i32) (i32.const 0))
(global $outputStart (mut i32) (i32.const 0))
(global $outputEnd (mut i32) (i32.const 0))
(global $upperA (mut i32) (i32.const 65))
(global $lowerA (mut i32) (i32.const 97))
(func (export "part1") (result i32)
(local $total i32)
call $initRead
i32.const 0
local.set $total
block $readBlock
loop $readLoop
call $findLine
call $canRead
i32.eqz
if
br $readBlock
end
;; check
call $measureLine
call $checkBuffersForDups
call $convertByteToPriority
local.get $total
i32.add
local.set $total
br $readLoop
end
end
call $initWrite
local.get $total
call $writeNum
call $closeWrite
)
(func (export "part2") (result i32)
(local $total i32)
call $initRead
i32.const 0
local.set $total
block $readBlock
loop $readLoop
call $findLine
call $canRead
i32.eqz
if
br $readBlock
end
;; get start & length of line 1
call $measureLine2
;; line 2
call $findLine
call $measureLine2
;; line 3
call $findLine
call $measureLine2
call $checkBuffersForDupsPart2
call $convertByteToPriority
local.get $total
i32.add
local.set $total
br $readLoop
end
end
call $initWrite
local.get $total
call $writeNum
call $closeWrite
)
(func $initRead
;; Read input length
i32.const 0
i32.load
;; Store input end byte
i32.const 4
i32.add
global.set $inputEnd
;; Set up start read location
i32.const 4
global.set $readLoc
)
(func $initWrite
(local $dup i32)
;; set up output end pointer
global.get $inputEnd
i32.const 4
i32.add
global.set $outputStart
global.get $outputStart
global.set $outputEnd
)
(func $closeWrite (result i32)
;; get the length to return
global.get $inputEnd
global.get $outputEnd
global.get $outputStart
i32.sub
i32.store
;; return location of length
global.get $inputEnd
)
;; assumes: readLoc is on the character at the beginning of a line
;; returns {locHalf1, locHalf2, lengthHalf}
;; updates: readLoc to next line
(func $measureLine (result i32 i32 i32)
(local $lineStart i32)
(local $midStart i32)
(local $length i32)
global.get $readLoc
local.set $lineStart
loop $readLoop
call $readChar
i32.eqz
if
nop
else
br $readLoop
end
end
;; compute half length
global.get $readLoc
local.get $lineStart
i32.sub
i32.const 2
i32.div_u
local.tee $length
;; compute middle length
local.get $lineStart
i32.add
local.set $midStart
local.get $lineStart
local.get $midStart
local.get $length
return
)
(func $measureLine2 (result i32 i32)
(local $lineStart i32)
(local $length i32)
global.get $readLoc
local.set $lineStart
loop $readLoop
call $readChar
i32.eqz
if
nop
else
br $readLoop
end
end
;; compute half length
global.get $readLoc
local.get $lineStart
i32.sub
local.set $length
local.get $lineStart
local.get $length
return
)
;; moves readLoc until referenced character is valid (not newline or null)
;; if position under readLoc is already valid, does not move
(func $findLine
loop $moveHead
call $canRead
i32.eqz
if
return
end
call $readChar
i32.eqz
br_if $moveHead
end
global.get $readLoc
i32.const 1
i32.sub
global.set $readLoc
)
;; returns the byte that is duplicated across both
(func $checkBuffersForDups (param $startA i32) (param $startB i32) (param $length i32) (result i32)
(local $currentChar i32)
(local $prevReadLoc i32)
;; preserve read loc
global.get $readLoc
local.set $prevReadLoc
;; set up read location
local.get $startA
global.set $readLoc
loop $checkChar
;; set up comparison locations
local.get $startB
local.get $length
;; get comparison char
call $readChar
local.tee $currentChar
call $findByteInBuffer
;; if not found
i32.eqz
if
;; check length
global.get $readLoc
local.get $startA
local.get $length
i32.add
;; if we still have chars
i32.lt_u
if
br $checkChar ;; loop back
end
end
end
;; reset the read loc
local.get $prevReadLoc
global.set $readLoc
;; return character (if found)
local.get $currentChar
return
)
;; returns 1 if byte occurs, 0 if not
(func $findByteInBuffer (param $start i32) (param $length i32) (param $byte i32) (result i32)
(local $currentChar i32)
(local $prevReadLoc i32)
;; preserve read loc
global.get $readLoc
local.set $prevReadLoc
;; set up read location
local.get $start
global.set $readLoc
loop $checkChar
;; get buffer char
call $readChar
local.tee $currentChar
;; check to search char
local.get $byte
i32.eq
if ;; found
;; reset the read loc
local.get $prevReadLoc
global.set $readLoc
i32.const 1
return
end
;; not found
;; check length
global.get $readLoc
local.get $start
local.get $length
i32.add
;; if we still have chars
i32.lt_u
if
br $checkChar ;; loop back
end
end
;; reset the read loc
local.get $prevReadLoc
global.set $readLoc
;; not found
i32.const 0
return
)
(func $checkBuffersForDupsPart2 (param $startA i32) (param $lengthA i32) (param $startB i32) (param $lengthB i32) (param $startC i32) (param $lengthC i32) (result i32)
(local $currentChar i32)
(local $prevReadLoc i32)
;; preserve read loc
global.get $readLoc
local.set $prevReadLoc
;; set up read location
local.get $startA
global.set $readLoc
loop $checkChar
;; get char in buffer A
call $readChar
local.set $currentChar
;; check buffer B
local.get $startB
local.get $lengthB
local.get $currentChar
call $findByteInBuffer
;; check buffer C
local.get $startC
local.get $lengthC
local.get $currentChar
call $findByteInBuffer
;; make sure found in both
i32.and
;; if not found
i32.eqz
if
;; check length
global.get $readLoc
local.get $startA
local.get $lengthA
i32.add
;; if we still have chars
i32.lt_u
if
br $checkChar ;; loop back
end
end
end
;; reset the read loc
local.get $prevReadLoc
global.set $readLoc
;; return character (if found)
local.get $currentChar
return
)
(func $convertByteToPriority (param $char i32) (result i32)
local.get $char
;; check if lower case
local.get $char
global.get $lowerA
i32.ge_u
if (result i32)
global.get $lowerA
else
global.get $upperA
i32.const 26
i32.sub
end
;; get alphabet offset
i32.sub
i32.const 1
i32.add
return
)
(func $readChar (result i32)
(local $charcode i32)
;; Make sure we're still inside the input area
block $readLocCheck
call $canRead
br_if $readLocCheck
i32.const 0
return
end
;; Read the character
global.get $readLoc
i32.load8_u
local.set $charcode
;; increment read location
global.get $readLoc
i32.const 1
i32.add
global.set $readLoc
;; check for newline
local.get $charcode
i32.const 0x0A
i32.eq
if
i32.const 0
return
end
local.get $charcode
return
)
(func $canRead (result i32)
global.get $readLoc
global.get $inputEnd
i32.lt_u
if (result i32)
i32.const 1
else
i32.const 0
end
)
(func $writeNum (param $number i32)
(local $digit i32)
;; store lowest digit
local.get $number
i32.const 10
i32.rem_u
local.set $digit
;; divide number by 10
local.get $number
i32.const 10
i32.div_u
local.tee $number
;; base case
i32.eqz
if
local.get $digit
call $writeDigit
return
end
;; recurse (to print other digits)
local.get $number
call $writeNum
;; print our digit
local.get $digit
call $writeDigit
)
(func $writeDigit (param $digit i32)
global.get $outputEnd
local.get $digit
i32.const 48
i32.add
i32.store
global.get $outputEnd
i32.const 1
i32.add
global.set $outputEnd
)
)