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
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
|
|
)
|
|
)
|