Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
Akuli committed Jan 4, 2025
1 parent 58ea13a commit 53c4cf2
Show file tree
Hide file tree
Showing 3 changed files with 276 additions and 0 deletions.
2 changes: 2 additions & 0 deletions examples/aoc2024/day24/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
asd.txt
asd.png
23 changes: 23 additions & 0 deletions examples/aoc2024/day24/notes.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
For all bits except the first and the last, there are 5 logic gates.

InputXor:
Inputs: xnn, ynn (e.g. x05, y05)
Output: 1 if exactly one input is on, 0 if neither or both

InputAnd:
Inputs: xnn, ynn (e.g. x05, y05)
Output: 1 if both inputs are on, 0 otherwise

OutputXor (missing for first bit, output taken directly from InputXor)
Inputs: result of InputXor, result of previous OverflowXor
Output: value of znn

OverflowAnd
Inputs: result of InputXor, overflow n-1
Output: 1 if we overflow with inputs 1,0 or 0,1 due to carrying
0 if we either don't overflow, or the overflow doesn't involve carrying

OverflowOr
Inputs: result of InputAnd, result of OverflowAnd
Output: 1 if we overflow, either with input 1,1 or through OverflowAnd
0 if we don't overflow
251 changes: 251 additions & 0 deletions examples/aoc2024/day24/part2.jou
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
# The following commands can be used to create asd.png, which contains a nice
# picture of the input as a graph:
#
# echo 'digraph G {' > asd.txt
# grep -- '->' sampleinput.txt | awk '{print $1 "->" $5 " [label="$2"]\n"$3"->"$5" [label="$2"]"}' >> asd.txt
# echo '}' >> asd.txt
# dot -Tpng -o asd.png asd.txt


import "stdlib/str.jou"
import "stdlib/ascii.jou"
import "stdlib/io.jou"


# The sum calculator consists of 45 adders and outputs 46 bits z00,z01,...,z45.
# Each adder (except the first and last) consists of the following 5 logic gates.
enum GateType:
# Inputs: xnn, ynn (e.g. x05 and y05)
# Output: 1 if inputs are 1,0 or 0,1
# 0 if inputs are 1,1 or 0,0
# Purpose: Used for both output and overflow checking
InputXor

# Inputs: xnn, ynn
# Output: 1 if inputs are 1,1
# 0 if inputs are anything else
# Purpose: Used to detect overflow
InputAnd

# Inputs: result of InputXor, carry bit from previous level
# Output: value of znn (the output bit of x+y)
# Purpose: Calculates the sum of the two numbers
OutputXor

# Inputs: result of InputXor, carry bit from previous level
# Output: 1 if we overflow when adding bits 1,0 or 0,1 due to carrying
# 0 if there's no overflow, or the overflow happens due to input 1,1
# Purpose: Creates intermediate value used when calculating overflow/carry
OverflowAnd

# Inputs: result of InputAnd, result of OverflowAnd
# Output: 1 if we overflow in any way, due to input 1,1 or carrying
# 0 if there is no overflow
# Purpose: This is the overflow/carry bit sent to the next adder
OverflowOr


class Gate:
inputs: byte[4][2]
output: byte[4]
level: int
gtype: GateType

def print(self) -> None:
if self->gtype == GateType::InputXor:
printf("InputXor")
elif self->gtype == GateType::InputAnd:
printf("InputAnd")
elif self->gtype == GateType::OutputXor:
printf("OutputXor")
elif self->gtype == GateType::OverflowAnd:
printf("OverflowAnd")
elif self->gtype == GateType::OverflowOr:
printf("OverflowOr")
else:
assert False

printf(": %s XOR %s -> %s", self->inputs[0], self->inputs[1], self->output)
if self->level != -1:
printf(" [level=%d]", self->level)
printf("\n")


def check_gtype_counts(gates: Gate*, ngates: int) -> None:
counts = [0, 0, 0, 0, 0]
for i = 0; i < ngates; i++:
counts[gates[i].gtype as int]++

assert counts[InputXor
input_xors = 0
input_ands = 0
output_xors = 0
overflow_ands = 0
overflow_ors = 0


# Due to carrying, every gate affects the last output z45 somehow.
#
def sort_gates_by_distance_from_z45


# Determines how badly the sum machine seems to be wired.
# Zero is a machine that is working as expected.
def calculate_score(gates: Gate*, ngates: int) -> int:
input_xors: Gate*[45]
input_ands: Gate*[45]
# First adder only has input xors (used for output) and input ands (used for overflow/carry)
output_xors: Gate*[44]
overflow_ands: Gate*[44]
overflow_ors: Gate*[44]

counts = [0, 0, 0, 0, 0]
for i = 0; i < ngates; i++:
counts[gates[i].gtype as int]++

assert counts[InputXor as int] == 45
assert counts[InputAnd as int] == 45
assert counts[OutputXor as int] == 44
assert counts[OverflowAnd as int] == 44
assert counts[OverflowOr as int] == 44

# Just put the things to the arrays, in any order
memset(counts, 0, sizeof(counts))


for i = 0; i < ngates; i++:
if gates[i].gtype == GateType::InputXor:
input_xors


def goes_to_1_gate(
gates: Gate*,
ngates: int,
wire: byte*,
gtype: GateType,
) -> bool:
found = False

for i = 0; i < ngates; i++:
if strcmp(gates[i].inputs[0], wire) == 0 or strcmp(gates[i].inputs[1], wire) == 0:
if gates[i].gtype != gtype:
return False
if found:
# it goes to two gates, both of the given type
return False
found = True

return found


def goes_to_2_gates(
gates: Gate*,
ngates: int,
wire: byte*,
gtype1: GateType,
gtype2: GateType,
) -> bool:
assert gtype1 != gtype2

found1 = False
found2 = False

for i = 0; i < ngates; i++:
if strcmp(gates[i].inputs[0], wire) == 0 or strcmp(gates[i].inputs[1], wire) == 0:
if gates[i].gtype == gtype1:
if found1:
return False
found1 = True
elif gates[i].gtype == gtype2:
if found2:
return False
found2 = True
else:
return False

return found1 and found2


def main() -> int:
gates: Gate[500]
ngates = 0

f = fopen("input", "r")
assert f != NULL

line: byte[100]
while fgets(line, sizeof(line) as int, f) != NULL:
trim_ascii_whitespace(line)
if line[0] == '\0':
# end of initial values, start of logic gates
break

g: Gate
op: byte[4]
while fscanf(f, "%3s %3s %3s -> %3s\n", &g.inputs[0], op, &g.inputs[1], &g.output) == 4:
# Inputs that don't come from other gates cannot be connected wrong, so
# the wiring mistakes don't mess this up.
if (
(g.inputs[0][0] == 'x' and g.inputs[1][0] == 'y')
or (g.inputs[0][0] == 'y' and g.inputs[1][0] == 'x')
):
# Handles input directly, so it must be InputXor or InputAnd
if strcmp(op, "XOR") == 0:
g.gtype = GateType::InputXor
elif strcmp(op, "AND") == 0:
g.gtype = GateType::InputAnd
else:
assert False
g.level = atoi(&g.inputs[0][1]) # Example: x05 --> 5
else:
if strcmp(op, "XOR") == 0:
g.gtype = GateType::OutputXor
elif strcmp(op, "AND") == 0:
g.gtype = GateType::OverflowAnd
elif strcmp(op, "OR") == 0:
g.gtype = GateType::OverflowOr
else:
assert False
g.level = -1 # unknown

assert ngates < sizeof(gates)/sizeof(gates[0])
gates[ngates++] = g

for i = 0; i < ngates; i++:
# Most InputAnd gates should connect to an OverflowOr.
if (
gates[i].gtype == GateType::InputAnd
and gates[i].level != 0
and not goes_to_1_gate(gates, ngates, gates[i].output, GateType::OverflowOr)
):
printf("sus1 %s\n", gates[i].output)

# First InputAnd produces the overflow bit directly, so it connects to
# the next OutputXor and OverflowAnd.
if (
gates[i].gtype == GateType::InputAnd
and gates[i].level == 0
and not goes_to_2_gates(gates, ngates, gates[i].output, GateType::OutputXor, GateType::OverflowAnd)
):
printf("sus2 %s\n", gates[i].output)

# Most InputXor gates should connect to an OutputXor and OverflowAnd.
if (
gates[i].gtype == GateType::InputXor
and gates[i].level != 0
and not goes_to_2_gates(gates, ngates, gates[i].output, GateType::OutputXor, GateType::OverflowAnd)
):
printf("sus3 %s\n", gates[i].output)

# First InputXor gate produces the first output directly.
if (
gates[i].gtype == GateType::InputXor
and gates[i].level == 0
and strcmp(gates[i].output, "z00") != 0
):
printf("sus4 %s\n", gates[i].output)



fclose(f)
return 0

0 comments on commit 53c4cf2

Please sign in to comment.