Some simple process of elimination reveals that g (pop y, x, then push value of ASCII character at location (x,y)) is just about the only way to get non-zero values onto the stack now. Since the stack is implicitly zero, the first g we hit with an empty stack will simply push the value of character at (0,0). However, unlike some more convenient languages (cough, cough, 05AB1E05AB1E), we don't have builtin operators to divide by 2 that we might use to reduce this value down to 1.
from collections import defaultdict # by convention, assume there is a 1 on top of the stack def gen_stack(stack): # goal is to produce stack:list[int] def gen_single(ch): if type(ch) is chr: ch = ord(ch) binary_decomp = [] cur = ch while cur > 0: # I know this is inefficient r = (1 << (cur.bit_length() - 1)) cur -= r binary_decomp.append(r) binary_decomp = binary_decomp[::-1] total = "" value = 1 for c in binary_decomp: total += ":" while value < c: total += ":+" value *= 2 return total + "+" * (len(binary_decomp) - 1) + "\\" return "".join(gen_single(c) for c in stack) # places string segment starting at (i,j) in given direction def place_segment(prog, i, j, segment, direction=(1,0)): for k, c in enumerate(segment): prog[i + k*direction[0], j + k*direction[1]] = c return len(segment) target_string = """,0123456789"~@!""" # [x,y], use implicit grid prog = defaultdict(lambda : ' ') W = 75 offset = 0 offset += place_segment(prog, offset, 0, ">g::g-") # ends at 6,0 excl # store this for 1-recovery prog[62,62] = "=" # now have 1 on stack target_stack = list(map(ord,target_string))[::-1] target_stack.extend([ord(","),2,10]) # write , to 2,10 target_stack.extend([ord("@"),2,12]) # write @ to 2,12 stack_generation = gen_stack(target_stack) stack_generation += "$" # pop the 1 stack_generation += "p" # place the @ stack_generation += "p" # place the , # program flow routing for i in range(8): prog[W,i] = "<" if (i % 2 == 1) else "v" prog[0,i] = "v" if (i % 2 == 1) else ">" # makes it more aesthetically pleasing offset += 1 row = 0 while len(stack_generation) > 0: # zig-zag segment placement until we're out if row % 2 == 0: place_segment(prog, offset, row, stack_generation[:W-offset-1]) stack_generation = stack_generation[W-offset-1:] else: place_segment(prog, W - 2, row, stack_generation[:W-offset-1], (-1,0)) stack_generation = stack_generation[W-offset-1:] row += 1 # first 8 rows were allocated for the stack generation # place the print-stack block place_segment(prog, 0, 9, "v <") place_segment(prog, 0, 10, " X") place_segment(prog, 0, 11, ">:|") place_segment(prog, 0, 12, " Y") # print prog max_y = max(k[1] for k in prog.keys()) max_x = lambda y : max([0, *[k[0] for k in prog.keys() if k[1] == y]]) # just so we don't print tons of extra spaces source = "\n".join("".join(prog[i,j] for i in range(max_x(j)+1)) for j in range(max_y+1)) print(source) # verify for c in target_string: if c in source: print("Failed check for",c) from collections import defaultdict # by convention, assume there is a 1 on top of the stack def gen_stack(stack): # goal is to produce stack:list[int] def gen_single(ch): if type(ch) is chr: ch = ord(ch) binary_decomp = [] cur = ch while cur > 0: # I know this is inefficient r = (1 << (cur.bit_length() - 1)) cur -= r binary_decomp.append(r) binary_decomp = binary_decomp[::-1] total = "" value = 1 for c in binary_decomp: total += ":" while value < c: total += ":+" value *= 2 return total + "+" * (len(binary_decomp) - 1) + "\\" return "".join(gen_single(c) for c in stack) # places string segment starting at (i,j) in given direction def place_segment(prog, i, j, segment, direction=(1,0)): for k, c in enumerate(segment): prog[i + k*direction[0], j + k*direction[1]] = c return len(segment) target_string = """,0123456789"~@!""" # [x,y], use implicit grid prog = defaultdict(lambda : ' ') W = 75 offset = 0 offset += place_segment(prog, offset, 0, ">g::g-") # ends at 6,0 excl # store this for 1-recovery prog[62,62] = "=" # now have 1 on stack target_stack = list(map(ord,target_string))[::-1] target_stack.extend([ord(","),2,10]) # write , to 2,10 target_stack.extend([ord("@"),2,12]) # write @ to 2,12 stack_generation = gen_stack(target_stack) stack_generation += "$" # pop the 1 stack_generation += "p" # place the @ stack_generation += "p" # place the , # program flow routing for i in range(8): prog[W,i] = "<" if (i % 2 == 1) else "v" prog[0,i] = "v" if (i % 2 == 1) else ">" # makes it more aesthetically pleasing offset += 1 row = 0 while len(stack_generation) > 0: # zig-zag segment placement until we're out if row % 2 == 0: place_segment(prog, offset, row, stack_generation[:W-offset-1]) stack_generation = stack_generation[W-offset-1:] else: place_segment(prog, W - 2, row, stack_generation[:W-offset-1], (-1,0)) stack_generation = stack_generation[W-offset-1:] row += 1 # first 8 rows were allocated for the stack generation # place the print-stack block place_segment(prog, 0, 9, "v <") place_segment(prog, 0, 10, " X") place_segment(prog, 0, 11, ">:|") place_segment(prog, 0, 12, " Y") # print prog max_y = max(k[1] for k in prog.keys()) max_x = lambda y : max([0, *[k[0] for k in prog.keys() if k[1] == y]]) # just so we don't print tons of extra spaces source = "\n".join("".join(prog[i,j] for i in range(max_x(j)+1)) for j in range(max_y+1)) print(source) # verify for c in target_string: if c in source: print("Failed check for",c)