# gbg.py - glass bead game
#
# CREATED 2021.05.17 ABS copied from randomEV.py
# MODIFIED 2021.05.18 ABS added labels
# MODIFIED 2021.05.24 ABS binary string representation
# MODIFIED 2021.05.26 ABS binary string representation on input
# MODIFIED 2021.05.27 ABS stop printing after size steps; added -m
# MODIFIED 2021.05.28 ABS added building outcome table
# MODIFIED 2021.05.31 ABS added -f final (target) value
# MODIFIED 2021.06.08 ABS added stop on target
# MODIFIED 2021.06.20 ABS debugged stop on target
# MODIFIED 2022.07.26 ABS fixed colors
# MODIFIED 2023.02.04 ABS tweaked; added scoring
# MODIFIED 2023.02.05 ABS added bit flip
# MODIFIED 2023.02.09 ABS added 'w' script command
# MODIFIED 2023.02.23 ABS tweaked table output
# MODIFIED 2023.03.06 ABS added -A & -X flags
# MODIFIED 2023.03.07 ABS debugged
# MODIFIED 2023.03.14 ABS fixed rule numbering
# MODIFIED 2023.03.16 ABS tweaked
# MODIFIED 2023.03.19 ABS fixed scoring
# MODIFIED 2023.04.05 ABS debugged steps in scripting
#
# To Do: [_] better HTML (titles, headers)
#
from __future__ import print_function
import sys
import argparse
import math
# import numpy as np
import random
# START: debugWrite
def debugWrite(flag, message, var):
if (flag):
print('gbg: DEBUG:', message, var, file=sys.stderr)
# END: debugWrite
# START: print_bead
def print_bead(bead):
if (bead == 0):
print("
![](zero.png) | ")
elif (bead == 1):
print("![](one.png) | ")
else:
print("_ | ")
# END: print_bead
# START: print_step
def print_step(step_line, step_num, rule_num, loop_size, binary_beads):
print("")
if (args.abbrevFlag == False):
for column in range(1, shown_size + 1):
print_bead(step_line[column])
bgcolor="WHITE"
if ((step_num == 0) or (step_num == "input")):
bgcolor="#80FF80"
if (binary_beads == args.final_binary):
bgcolor="PINK"
print("", step_num, " | ")
print("", rule_num, " | ")
print("", binary_beads, " | ")
if (args.xorFlag == True):
# debugWrite(args.debugFlag, "args.final_binary = ", args.final_binary)
# debugWrite(args.debugFlag, "binary_beads = ", binary_beads)
# target_binary = "0b" + args.final_binary
# current_binary = "0b" + binary_beads
target_binary = args.final_binary
current_binary = binary_beads
debugWrite(args.debugFlag, "target_binary =", target_binary)
debugWrite(args.debugFlag, "current_binary =", current_binary)
# debugWrite(args.debugFlag, "type(current_binary) =", type(current_binary))
# A = 0b1100
# debugWrite(args.debugFlag, "type(A) =", type(A))
# debugWrite(args.debugFlag, "int(target_binary, 2) =", int(target_binary, 2))
# debugWrite(args.debugFlag, "int(current_binary, 2) =", int(current_binary, 2))
differ = bin(int(target_binary, 2)^int(current_binary, 2))
debugWrite(args.debugFlag, "differ = ", differ)
print("", differ.count("1"), "bits | ")
print("
")
if (loop_size == 0):
# print to table file
if ((binary_beads == args.final_binary) and (step > 0)):
print(args.binary_start, binary_beads, step, rule_number, file=tf, sep=",")
# END: print_step
# START: apply_rule
def apply_rule(a, b, c, rule):
#
cluster = a*4 + b*2 + c
index = 2**cluster
mask = (int(index) & int(flip_byte(rule)))
result = mask/index
return result
# END: apply_rule
# START: flip_byte
def flip_byte(input_byte):
#
flipped_string = ""
for i in range (0,8):
reverse_i = i
temp_mask = 2**reverse_i
if ((input_byte & temp_mask) == 0):
flipped_string = flipped_string + "0"
else:
flipped_string = flipped_string + "1"
flipped_byte = int("0b" + flipped_string, 2)
return flipped_byte
# END: flip_byte
# START: parse_binary_array
def parse_binary_array(binary_string, binary_array, size):
for column in range(0, size):
binary_array[column] = int(binary_string[column])
# END: parse_binary_array
# START: encode_binary_string
def encode_binary_string(binary_array, size):
temp_string = ""
for column in range(0, size):
if (binary_array[column] == 0):
temp_string = temp_string + '0'
else:
temp_string = temp_string + '1'
return(temp_string)
# END: encode_binary_string
# START: running (main)
#
parser = argparse.ArgumentParser(description='explore or play the Glass Bead Game; see: https://people.well.com/user/abs/Cyb/archive/c3m_1701.html#sec_1')
parser.add_argument('-D', '--DEBUG', dest='debugFlag', action='store_true',
help='debug write')
parser.add_argument('-A', '--ABBREV', dest='abbrevFlag', action='store_true',
help='abbreviated output (no beads)')
parser.add_argument('-X', '--XOR', dest='xorFlag', action='store_true',
help='xor current and target and count bits')
parser.add_argument('-b', '--binary_start', dest='binary_start', default='0101010101', action='store',
help='starting bead pattern in binary')
parser.add_argument('-f', '--final_binary', dest='final_binary', default='1010101010', action='store',
help='final bead pattern in binary')
parser.add_argument('-m', '--max_steps', dest='max_steps', default=8192, action='store',
help='maximum number of steps')
parser.add_argument('-n', '--num_steps', dest='num_steps', default=8, action='store',
help='number of steps to print')
parser.add_argument('-r', '--rule', dest='rule', default=32, action='store',
help='rule number of 2D binary cellular automaton')
parser.add_argument('-s', '--size', dest='size', default=10, action='store',
help='size of string of beads')
parser.add_argument('-t', '--table_file', dest='table_file', default="table_map.txt", action='store',
help='table file')
parser.add_argument('-L', '--Loop', dest='Loop', default=False, action='store_true',
help='stop when loop detected')
parser.add_argument('-T', '--Target_stop', dest='Target_stop', default=False, action='store_true',
help='stop when target reached')
parser.add_argument('-S', '--Script', dest='Script', default=False, action='store_true',
help='take script commands from standard input')
parser.add_argument('-W', '--Wrap', dest='Wrap', default=False, action='store_true',
help='wrap each line')
args = parser.parse_args()
num_steps = int(args.num_steps)
max_steps = int(args.max_steps)
rule_number = int(args.rule)
shown_size = int(args.size)
rule_score = shown_size*shown_size
flip_score = shown_size*shown_size*shown_size
true_size = shown_size + 2
Wrap_lines = (args.Wrap == True)
Script_control = (args.Script == True)
Target_stop = (args.Target_stop == True)
cumulative_score = 0
rule_changes = 0
bit_flips = 0
PrintRow = True
debugWrite(args.debugFlag, "======== num_steps =", num_steps)
debugWrite(args.debugFlag, "rule_number =", rule_number)
debugWrite(args.debugFlag, "shown_size =", shown_size)
debugWrite(args.debugFlag, "true_size =", true_size)
debugWrite(args.debugFlag, "args.table_file =", args.table_file)
debugWrite(args.debugFlag, "args.binary_start =", args.binary_start)
debugWrite(args.debugFlag, "lenght of args.binary_start =", len(args.binary_start))
debugWrite(args.debugFlag, "Wrap_lines =", Wrap_lines)
debugWrite(args.debugFlag, "Script_control =", Script_control)
debugWrite(args.debugFlag, "rule_score =", rule_score)
debugWrite(args.debugFlag, "flip_score =", flip_score)
tf = open(args.table_file, 'w')
# 9 = bad value
start_array = []
for column in range(0, true_size):
start_array.append(9)
start_binary_string = '0' + args.binary_start + '0'
# debugWrite(args.debugFlag, "start_binary_string =", start_binary_string)
parse_binary_array(start_binary_string, start_array, true_size)
if (Wrap_lines):
start_array[0] = start_array[-2]
start_array[-1] = start_array[1]
else:
start_array[0] = 0
start_array[-1] = 0
# debugWrite(args.debugFlag, "start_array =", start_array)
prev_array = []
step_array = []
for column in range(0, true_size):
prev_array.append(int(start_array[column]))
if (Wrap_lines):
prev_array[0] = prev_array[-2]
prev_array[-1] = prev_array[1]
else:
prev_array[0] = 0
prev_array[-1] = 0
debugWrite(args.debugFlag, "start_array: ", start_array)
step = 0
debugWrite(args.debugFlag, ">>>>>>>> step =", step)
true_beads = encode_binary_string(start_array, true_size)
display_beads = true_beads[1:-1]
print("starting beads =", display_beads, "; target beads =", args.final_binary, "
")
print("")
print("
")
print("")
if (args.abbrevFlag == False):
for column in range(1, shown_size + 1):
print("", column, " | ")
print("step or action | ")
print("rule/bit number | ")
print("binary beads | ")
if (args.xorFlag == True):
print("xor current and final, count bits | ")
print("
")
print_step(start_array, "input", "-", 0, display_beads)
if (display_beads == args.final_binary):
# found target
print("
")
print("target found after 0 steps")
print("score = 0")
sys.exit()
# KLUDGE
for column in range(0, true_size):
step_array.append(-1)
step_stack = []
step_stack.append(encode_binary_string(start_array, true_size))
stack_size = 1
# script case
if (Script_control == True):
cumulative_steps = 1
rule_number = 0
Stepping = False
#
# process script
#
for line in sys.stdin:
stripped_line = line.rstrip('\n')
fields = stripped_line.split(" ")
debugWrite(args.debugFlag, "fields =", fields)
if (fields[0] == 'r'):
rule_number = int(fields[1])
if (Stepping):
cumulative_score = cumulative_score + rule_score
debugWrite(args.debugFlag, "r cumulative_score =", cumulative_score)
rule_changes = rule_changes + 1
debugWrite(args.debugFlag, "new rule_number =", rule_number)
# end if
if (fields[0] == 'w'):
int_wrap_lines = int(fields[1])
if (int_wrap_lines == 0):
Wrap_lines = False
else:
Wrap_lines = True
if (Stepping):
cumulative_score = cumulative_score + rule_score
debugWrite(args.debugFlag, "w cumulative_score =", cumulative_score)
rule_changes = rule_changes + 1
debugWrite(args.debugFlag, "new Wrap_lines =", Wrap_lines)
if (PrintRow):
true_beads = encode_binary_string(step_array, true_size)
display_beads = true_beads[1:-1]
print_step(step_array, "wrap", int_wrap_lines, 0, display_beads)
# end if
if (fields[0] == 'f'):
Stepping = True
flip_bit = int(fields[1])
debugWrite(args.debugFlag, "flipping bit", flip_bit)
bit_flips = bit_flips + 1
cumulative_score = cumulative_score + flip_score
debugWrite(args.debugFlag, "f cumulative_score =", cumulative_score)
if (step_array[flip_bit] == 0):
step_array[flip_bit] = 1
else:
step_array[flip_bit] = 0
if (PrintRow):
true_beads = encode_binary_string(step_array, true_size)
display_beads = true_beads[1:-1]
print_step(step_array, "flip", flip_bit, 0, display_beads)
if (display_beads == args.final_binary):
# found target
print("")
print("")
print("target found; steps (1 point): ", cumulative_steps - 1, "; rule/wrap changes (", rule_score, " points): ", rule_changes, "; bit flips (", flip_score, " points): ", bit_flips, "", sep="")
print("")
print("score = ", cumulative_score, "")
sys.exit()
# end elif
if (fields[0] == 's'):
Stepping = True
max_steps = int(fields[1])
cumulative_score = cumulative_score + max_steps
debugWrite(args.debugFlag, "s cumulative_score =", cumulative_score)
debugWrite(args.debugFlag, "new max_steps =", max_steps)
# process steps in script
for step in range(0, max_steps):
debugWrite(args.debugFlag, ">>>>>>>> step =", step)
for column in range(1, shown_size + 1):
step_array[column] = apply_rule(prev_array[column - 1], prev_array[column], prev_array[column + 1], rule_number)
if (Wrap_lines):
step_array[0] = step_array[-2]
step_array[-1] = step_array[1]
else:
step_array[0] = 0
step_array[-1] = 0
for column in range(0, true_size):
prev_array[column] = step_array[column]
debugWrite(args.debugFlag, "step_array: ", step_array)
if (PrintRow):
true_beads = encode_binary_string(step_array, true_size)
display_beads = true_beads[1:-1]
print_step(step_array, cumulative_steps, rule_number, 0, display_beads)
if (display_beads == args.final_binary):
# found target
print("")
print("")
print("target found after", cumulative_steps, "steps,", rule_changes, "rule/wrap changes and", bit_flips, "bit flips")
print("")
print("score = ", cumulative_score, "")
sys.exit()
cumulative_steps = cumulative_steps + 1
# end for step
# end if
if ((fields[0] != 's') and (fields[0] != 'f') and (fields[0] != 'r') and (fields[0] != 'w')):
print("ERROR: illegal script command:", fields[0], file=sys.stderr)
sys.exit()
# end else
# end for line
print("")
sys.exit()
# no script case
for step in range(1, max_steps):
debugWrite(args.debugFlag, ">>>>>>>> step =", step)
if (step % 1000 == 0):
print("++++++++ step =", step, file=sys.stderr)
if (step > num_steps):
# just crossed the line
if (PrintRow):
print("* |
")
PrintRow = False
for column in range(1, shown_size + 1):
step_array[column] = apply_rule(prev_array[column - 1], prev_array[column], prev_array[column + 1], rule_number)
if (Wrap_lines):
step_array[0] = step_array[-2]
step_array[-1] = step_array[1]
else:
step_array[0] = 0
step_array[-1] = 0
for column in range(0, true_size):
prev_array[column] = step_array[column]
debugWrite(args.debugFlag, "step_array: ", step_array)
step_stack.append(encode_binary_string(step_array, true_size))
stack_size = stack_size + 1
loop_size = 0
for depth in range(0, stack_size - 1):
# debugWrite(args.debugFlag, "depth =", depth)
# debugWrite(args.debugFlag, "step_stack[depth] =", step_stack[depth])
# debugWrite(args.debugFlag, "step_stack[-1] =", step_stack[-1])
# check if latest step is a repeat
if (step_stack[-1] == step_stack[depth]):
PrintRow = True
loop_size = stack_size - depth - 1
debugWrite(args.debugFlag, "loop_size =", loop_size)
break
if (PrintRow):
true_beads = encode_binary_string(step_array, true_size)
display_beads = true_beads[1:-1]
print_step(step_array, step, rule_number, loop_size, display_beads)
if (display_beads == args.final_binary):
# found target
print("")
print("target found after", step, "steps")
sys.exit()
if (loop_size > 0):
break
# debugWrite(args.debugFlag, "step_stack =", step_stack)
print("")
if (loop_size == 0):
print("no loop after", max_steps, "steps")
print(args.binary_start, display_beads, "U", rule_number, file=tf, sep=",")
elif (loop_size > 0):
print("transient =", step - loop_size, " ")
print("loop =", loop_size, "")
print("")
#
# END: running (main)