Skip to content

Instantly share code, notes, and snippets.

@joostrijneveld
Created December 27, 2025 11:46
Show Gist options
  • Select an option

  • Save joostrijneveld/4f8c3c5052e8a367e4f400ec69eaf25a to your computer and use it in GitHub Desktop.

Select an option

Save joostrijneveld/4f8c3c5052e8a367e4f400ec69eaf25a to your computer and use it in GitHub Desktop.
Solver script for Blue Prince Mora Jai boxes
from collections import deque
import sys
from statistics import multimode
# een mora jai box is een combinatie van 4 hoekjes-targets en 9 velden
ORANJE = 0
ZWART = 1
GROEN = 2
PAARS = 3
GEEL = 4
ROOD = 5
GRIJS = 6
WIT = 7
ROZE = 8
BLAUW = 9
cmaps = [
"ORANJE",
"ZWART ",
"GROEN ",
"PAARS ",
"GEEL ",
"ROOD ",
"GRIJS ",
"WIT ",
"ROZE ",
"BLAUW ",
]
TOPLEFT = 9
TOPRIGHT = 10
BOTLEFT = 11
BOTRIGHT = 12
# dus een lijst van 13 van deze units
# eerst 4 targets en dan 9 boxjes
# in een map houden we per node bij vanaf welke andere node we er kwamen, en met welke box
path = {}
visited = set()
def is_solution(box):
return (
box[TOPLEFT] == box[0]
and box[TOPRIGHT] == box[2]
and box[BOTLEFT] == box[6]
and box[BOTRIGHT] == box[8]
)
def render_box(box):
for row in [[0, 1, 2], [3, 4, 5], [6, 7, 8]]:
for i in row:
print(cmaps[box[i]], end=" ")
print()
def render_path(previous, previous_click, box):
boxes = []
sequence = []
i = 0
while box in previous:
if previous[box] == "START":
break
boxes.append(previous[box])
sequence.append(previous_click[box])
box = previous[box]
i += 1
for i, (box, n) in enumerate(reversed(list(zip(boxes, sequence)))):
print("#", i)
render_box(box)
print("click", n + 1, cmaps[box[n]])
print("")
for i, (box, n) in enumerate(reversed(list(zip(boxes, sequence)))):
print("click", n + 1, cmaps[box[n]])
def swap(box, a, b):
box[a], box[b] = box[b], box[a]
def get_neighbours(field):
neighbours = []
y = field // 3
x = field % 3
if y > 0:
neighbours.append((y - 1) * 3 + x)
if y < 2:
neighbours.append((y + 1) * 3 + x)
if x > 0:
neighbours.append(y * 3 + (x - 1))
if x < 2:
neighbours.append(y * 3 + (x + 1))
return neighbours
def process_color(box, field, color=None):
# voor blauw willen we de kleur kunnen meegeven
if color == None:
color = box[field]
if color == BLAUW:
if box[4] == BLAUW:
return box
return process_color(box, field, color=box[4])
newbox = list(box)
y = field // 3
x = field % 3
if color == ORANJE:
multimodes = multimode([box[f] for f in get_neighbours(field)])
# als er precies eentje de meeste stemmen heeft wordt het die
if len(multimodes) == 1:
newbox[field] = multimodes[0]
elif color == ZWART:
newbox[y * 3 + 1] = box[y * 3 + 0]
newbox[y * 3 + 2] = box[y * 3 + 1]
newbox[y * 3 + 0] = box[y * 3 + 2]
elif color == GROEN:
swaptargets = [8, 7, 6, 5, 4, 3, 2, 1, 0]
swap(newbox, field, swaptargets[field])
elif color == PAARS:
if y < 2: # onderste rij doet paars niks
swap(newbox, y * 3 + x, (y + 1) * 3 + x)
elif color == GEEL:
if y > 0: # bovenste rij doet geel niks
swap(newbox, y * 3 + x, (y - 1) * 3 + x)
elif color == ROOD:
for i in range(9):
if box[i] == ZWART:
newbox[i] = ROOD
if box[i] == WIT:
newbox[i] = ZWART
elif color == GRIJS:
pass
elif color == WIT:
newbox[field] = GRIJS
for f in get_neighbours(field):
if newbox[f] == WIT:
newbox[f] = GRIJS
elif newbox[f] == GRIJS:
newbox[f] = box[field]
elif color == ROZE:
# 0 1 2
# 3 4 5
# 6 7 8
if field == 0:
seq = [1, 4, 3]
if field == 1:
seq = [0, 2, 5, 4, 3]
if field == 2:
seq = [1, 5, 4]
if field == 3:
seq = [0, 1, 4, 7, 6]
if field == 4:
seq = [0, 1, 2, 5, 8, 7, 6, 3]
if field == 5:
seq = [1, 2, 8, 7, 4]
if field == 6:
seq = [3, 4, 7]
if field == 7:
seq = [3, 4, 5, 8, 6]
if field == 8:
seq = [4, 5, 7]
oldseq = seq[-1:] + seq[0:-1]
for a, b in zip(seq, oldseq):
newbox[a] = box[b]
return tuple(newbox)
def bfs(start):
box = tuple(start)
queue = deque([box])
visited = set({})
previous = {box: "START"}
previous_click = {}
while queue:
box = queue.popleft()
if box in visited:
continue
visited.add(box)
if is_solution(box):
print("Found solution")
render_path(previous, previous_click, box)
sys.exit(0)
for i in range(9):
newbox = process_color(box, i)
if newbox != box:
if newbox not in previous:
previous[newbox] = box
previous_click[newbox] = i
queue.append(newbox)
def flatten_box(box):
return [x for row in box for x in row]
VESTIBULE = [
GRIJS,
GROEN,
GRIJS,
ORANJE,
ZWART,
ROOD,
ZWART,
WIT,
PAARS,
ZWART,
PAARS,
ORANJE,
ROOD,
]
TROPHYROOM = [
WIT,
ROOD,
GRIJS,
GRIJS,
ROZE,
GRIJS,
ZWART,
ROOD,
WIT,
WIT,
WIT,
WIT,
WIT,
]
PUMPROOM = [
PAARS,
BLAUW,
PAARS,
ZWART,
GROEN,
ORANJE,
ZWART,
GROEN,
ORANJE,
PAARS,
PAARS,
PAARS,
PAARS,
]
BILLIARD = [
ROZE,
WIT,
ROZE,
BLAUW,
GRIJS,
BLAUW,
ROZE,
ROOD,
ROZE,
ROOD,
ROOD,
ROOD,
ROOD,
]
FOYER = [
GROEN,
GROEN,
GROEN,
GRIJS,
ORANJE,
ORANJE,
BLAUW,
GRIJS,
PAARS,
GROEN,
GROEN,
GROEN,
GROEN,
]
box = BILLIARD
assert len(box) == 13
bfs(box)
# flatbox = flatten_box(box)
# bfs(flatbox)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment