318 lines
5.6 KiB
Go
318 lines
5.6 KiB
Go
package sixteen
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
)
|
|
|
|
const (
|
|
literalType = 4
|
|
lengthType15 = 0
|
|
lengthType11 = 1
|
|
)
|
|
|
|
var hexToBin = map[string]string{
|
|
"0": "0000",
|
|
"1": "0001",
|
|
"2": "0010",
|
|
"3": "0011",
|
|
"4": "0100",
|
|
"5": "0101",
|
|
"6": "0110",
|
|
"7": "0111",
|
|
"8": "1000",
|
|
"9": "1001",
|
|
"A": "1010",
|
|
"B": "1011",
|
|
"C": "1100",
|
|
"D": "1101",
|
|
"E": "1110",
|
|
"F": "1111",
|
|
}
|
|
|
|
type packet struct {
|
|
packet string
|
|
version int
|
|
typeID int
|
|
content int
|
|
subpackets []packet
|
|
}
|
|
|
|
func (p *packet) versionSum() int {
|
|
sum := p.version
|
|
|
|
for _, s := range p.subpackets {
|
|
sum += s.versionSum()
|
|
}
|
|
|
|
return sum
|
|
}
|
|
|
|
func (p *packet) sum() int {
|
|
sum := 0
|
|
for _, p := range p.subpackets {
|
|
sum += p.value()
|
|
}
|
|
return sum
|
|
}
|
|
|
|
func (p *packet) product() int {
|
|
product := 1
|
|
for _, p := range p.subpackets {
|
|
product *= p.value()
|
|
}
|
|
return product
|
|
}
|
|
|
|
func (p *packet) min() int {
|
|
min := 0
|
|
for i, p := range p.subpackets {
|
|
if i == 0 || p.content < min {
|
|
min = p.value()
|
|
}
|
|
}
|
|
return min
|
|
}
|
|
|
|
func (p *packet) max() int {
|
|
max := 0
|
|
for i, p := range p.subpackets {
|
|
if i == 0 || p.content > max {
|
|
max = p.value()
|
|
}
|
|
}
|
|
return max
|
|
}
|
|
|
|
func (p *packet) greaterThan() int {
|
|
result := 0
|
|
if p.subpackets[0].value() > p.subpackets[1].value() {
|
|
result = 1
|
|
}
|
|
return result
|
|
}
|
|
|
|
func (p *packet) lessThan() int {
|
|
result := 0
|
|
if p.subpackets[0].value() < p.subpackets[1].value() {
|
|
result = 1
|
|
}
|
|
return result
|
|
}
|
|
|
|
func (p *packet) equalTo() int {
|
|
result := 0
|
|
if p.subpackets[0].value() == p.subpackets[1].value() {
|
|
result = 1
|
|
}
|
|
return result
|
|
}
|
|
|
|
func (p *packet) value() int {
|
|
switch p.typeID {
|
|
case 0:
|
|
return p.sum()
|
|
case 1:
|
|
return p.product()
|
|
case 2:
|
|
return p.min()
|
|
case 3:
|
|
return p.max()
|
|
case 5:
|
|
return p.greaterThan()
|
|
case 6:
|
|
return p.lessThan()
|
|
case 7:
|
|
return p.equalTo()
|
|
}
|
|
|
|
return p.content
|
|
}
|
|
|
|
func hexToBinary(input string) string {
|
|
output := ""
|
|
for _, r := range input {
|
|
output += hexToBin[string(r)]
|
|
}
|
|
|
|
return output
|
|
}
|
|
|
|
func parseHex(input string) packet {
|
|
binary := hexToBinary(input)
|
|
return parse(binary)
|
|
}
|
|
|
|
func parse(input string) packet {
|
|
b := packet{}
|
|
b.packet = input
|
|
|
|
b.version, b.typeID = parseVersionType(input)
|
|
content := input[6:]
|
|
|
|
switch b.typeID {
|
|
case literalType:
|
|
b.content, _ = parseLiteral(content)
|
|
default:
|
|
b.subpackets, _ = parseOperator(content)
|
|
}
|
|
|
|
return b
|
|
}
|
|
|
|
func parseVersionType(input string) (int, int) {
|
|
var version, typeID string
|
|
if count, err := fmt.Sscanf(input, "%3s%3s", &version, &typeID); count != 2 || err != nil {
|
|
fmt.Println("Unable to parse", input, err)
|
|
return -1, -1
|
|
}
|
|
|
|
v, err := strconv.ParseInt(version, 2, 64)
|
|
if err != nil {
|
|
fmt.Println("Unable to parse version from", version, err)
|
|
return -1, -1
|
|
}
|
|
|
|
t, err := strconv.ParseInt(typeID, 2, 64)
|
|
if err != nil {
|
|
fmt.Println("Unable to parse type from", typeID, err)
|
|
return -1, -1
|
|
}
|
|
|
|
return int(v), int(t)
|
|
}
|
|
|
|
func parsePackets(input string) []packet {
|
|
packets := []packet{}
|
|
|
|
remaining := input
|
|
for remaining != "" {
|
|
version, typeID := parseVersionType(remaining)
|
|
|
|
p := packet{
|
|
version: version,
|
|
typeID: typeID,
|
|
}
|
|
|
|
switch p.typeID {
|
|
case literalType:
|
|
value, length := parseLiteral(remaining[6:])
|
|
p.content = value
|
|
p.packet = remaining[:length+6]
|
|
remaining = remaining[length+6:]
|
|
default:
|
|
l := 0
|
|
p.subpackets, l = parseOperator(remaining[6:])
|
|
if len(remaining) == l+6 {
|
|
remaining = ""
|
|
} else {
|
|
remaining = remaining[l+6:]
|
|
}
|
|
}
|
|
|
|
packets = append(packets, p)
|
|
}
|
|
|
|
return packets
|
|
}
|
|
|
|
func parseNPackets(input string, count int) ([]packet, int) {
|
|
length := len(input)
|
|
packets := []packet{}
|
|
|
|
remaining := input
|
|
for len(packets) != count {
|
|
version, typeID := parseVersionType(remaining)
|
|
|
|
p := packet{
|
|
version: version,
|
|
typeID: typeID,
|
|
}
|
|
|
|
parsedLength := 0
|
|
switch p.typeID {
|
|
case literalType:
|
|
value := 0
|
|
value, parsedLength = parseLiteral(remaining[6:])
|
|
p.content = value
|
|
p.packet = remaining[:parsedLength+6]
|
|
default:
|
|
p.subpackets, parsedLength = parseOperator(remaining[6:])
|
|
}
|
|
|
|
if len(remaining) == parsedLength+6 {
|
|
remaining = ""
|
|
} else {
|
|
remaining = remaining[parsedLength+6:]
|
|
}
|
|
|
|
packets = append(packets, p)
|
|
}
|
|
|
|
return packets, length - len(remaining)
|
|
}
|
|
|
|
func parseOperator(input string) ([]packet, int) {
|
|
length := 1
|
|
subPackets := []packet{}
|
|
var lType int
|
|
var remainder string
|
|
if count, err := fmt.Sscanf(input, "%1d%s", &lType, &remainder); count != 2 || err != nil {
|
|
fmt.Println("Unable to parse operator type", input)
|
|
return subPackets, length
|
|
}
|
|
|
|
switch lType {
|
|
case lengthType15:
|
|
lengthBits, _ := strconv.ParseInt(string(remainder[:15]), 2, 64)
|
|
subs := parsePackets(remainder[15 : 15+lengthBits])
|
|
subPackets = append(subPackets, subs...)
|
|
length += 15 + int(lengthBits)
|
|
case lengthType11:
|
|
lengthBits, _ := strconv.ParseInt(string(remainder[:11]), 2, 64)
|
|
subs, l := parseNPackets(remainder[11:], int(lengthBits))
|
|
subPackets = append(subPackets, subs...)
|
|
length += 11 + l
|
|
}
|
|
|
|
return subPackets, length
|
|
}
|
|
|
|
func parseLiteral(input string) (int, int) {
|
|
literal := ""
|
|
length := 0
|
|
for i := 0; i < len(input); i += 5 {
|
|
literal += string(input[i+1]) + string(input[i+2]) + string(input[i+3]) + string(input[i+4])
|
|
length += 5
|
|
|
|
if input[i] == '0' {
|
|
break
|
|
}
|
|
}
|
|
|
|
v, err := strconv.ParseInt(literal, 2, 64)
|
|
if err != nil {
|
|
fmt.Println("Unable to parse", literal, err)
|
|
return 0, -1
|
|
}
|
|
|
|
return int(v), length
|
|
}
|
|
|
|
func load(filename string) packet {
|
|
b := packet{}
|
|
|
|
file, err := os.Open(filename)
|
|
if err != nil {
|
|
return b
|
|
}
|
|
defer file.Close()
|
|
scanner := bufio.NewScanner(file)
|
|
for scanner.Scan() {
|
|
b = parseHex(scanner.Text())
|
|
}
|
|
return b
|
|
}
|