<jupyter_start><jupyter_text>Build names mappingTo make it a little easier to check that I'm using the correct guids, construct a mapping from names back to `guid`.Note: this adds a constraint that no two nodes have the same name, which should not be enforced for general semantic graphs.<jupyter_code>names = {}
for node in graph:
    for edge in node:
        if edge.guid == "169a81aefca74e92b45e3fa03c7021df":
            value = node[edge].value
            if value in names:
                raise ValueError('name: "{}" defined twice'.format(value))
            names[value] = node
     
names["ctor"]
def name_to_guid(name):
    if name not in names:
        return None
    node = names[name]
    if not hasattr(node, "guid"):
        return None
    return node.guid<jupyter_output><empty_output><jupyter_text>Pikov ClassesThese classes are the core resources used in defining a "Pikov" file.Note: ideally these classes could be derived from the graph itself, but I don't (yet) encode type or field information in the `pikov.json` semantic graph.<jupyter_code>from pikov.sprite import Bitmap, Clip, Frame, FrameList, Resource, Transition<jupyter_output><empty_output><jupyter_text>GamekittyCreate instances of the Pikov classes to define a concrete Pikov graph, based on my "gamekitty" animations. Load the spritesheetIn the previous notebook, we chopped the spritesheet into bitmaps. Find those and save them to an array so that they can be indexed as they were in the original PICO-8 gamekitty doodle.<jupyter_code>resource = Resource(graph, guid=name_to_guid("spritesheet"))

spritesheet = []
for row in range(16):
    for column in range(16):
        sprite_number = row * 16 + column
        bitmap_name = "bitmap[{}]".format(sprite_number)
        bitmap = Bitmap(graph, guid=name_to_guid(bitmap_name))
        spritesheet.append(bitmap)<jupyter_output><empty_output><jupyter_text>Create frames for each "clip"Each animation is defined in terms of sprite numbers. Sometimes a clip should loop, but sometimes it's only used as a transition between looping clips.<jupyter_code>def find_nodes(graph, ctor, cls):
    nodes = set()
    # TODO: With graph formats that have indexes, there should be a faster way.
    for node in graph:
        if node[names["ctor"]] == ctor:
            node = cls(graph, guid=node.guid)
            nodes.add(node)
    return nodes


def find_frames(graph):
    return find_nodes(graph, names["frame"], Frame)


def find_transitions(graph):
    return find_nodes(graph, names["transition"], Transition)


def find_absorbing_frames(graph):
    transitions = find_transitions(graph)
    target_frames = set()
    source_frames = set()
    for transition in transitions:
        target_frames.add(transition.target.guid)
        source_frames.add(transition.source.guid)
    return target_frames - source_frames  # In but not out. Dead end!
MICROS_12_FPS = int(1e6 / 12)  # 12 frames per second
MICROS_24_FPS = int(1e6 / 24)


def connect_frames(graph, transition_name, source, target):
    transition = Transition(graph, guid=name_to_guid(transition_name))
    transition.name = transition_name
    transition.source = source
    transition.target = target
    return transition

sit = Clip(graph, guid=name_to_guid("clip[sit]"))
sit
sit_to_stand = Clip(graph, guid=name_to_guid("clip[sit_to_stand]"))
sit_to_stand
stand_waggle= Clip(graph, guid=name_to_guid("clip[stand_waggle]"))
stand_waggle
connect_frames(
    graph,
    "transitions[sit_to_stand, stand_waggle]",
    sit_to_stand[-1],
    stand_waggle[0])
stand_to_sit = Clip(graph, guid=name_to_guid("clip[stand_to_sit]"))
stand_to_sit
connect_frames(
    graph,
    "transitions[stand_waggle, stand_to_sit]",
    stand_waggle[-1],
    stand_to_sit[0])
connect_frames(
    graph,
    "transitions[stand_to_sit, sit]",
    stand_to_sit[-1],
    sit[0])
sit_paw = Clip(graph, guid=name_to_guid("clip[sit_paw]"))
sit_paw
connect_frames(
    graph,
    "transitions[sit_paw, sit]",
    sit_paw[-1],
    sit[0])
connect_frames(
    graph,
    "transitions[sit, sit_paw]",
    sit[-1],
    sit_paw[0])
sit_to_crouch = Clip(graph, guid=name_to_guid("clip[sit_to_crouch]"))
connect_frames(
    graph,
    "transitions[sit, sit_to_crouch]",
    sit[-1],
    sit_to_crouch[0])
crouch = Clip(graph, guid=name_to_guid("clip[crouch]"))
connect_frames(
    graph,
    "transitions[sit_to_crouch, crouch]",
    sit_to_crouch[-1],
    crouch[0])
crouch_to_sit = Clip(graph, guid=name_to_guid("clip[crouch_to_sit]"))
connect_frames(
    graph,
    "transitions[crouch_to_sit, sit]",
    crouch[-1],
    crouch_to_sit[0])
connect_frames(
    graph,
    "transitions[crouch_to_sit, sit]",
    crouch_to_sit[-1],
    sit[0])
find_absorbing_frames(graph)
graph.save()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.10469011725293133, 'fraction_numerical': 0.01193467336683417, 'mean_word_length': 4.313681868743048, 'pattern_counts': {'":': 1, '<': 17, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '4', 'dir': 'jupyter-structured-clean-dedup', 'id': '220526', 'license': 'Apache-2.0', 'n_tokens_mistral': 1679, 'n_tokens_neox': 1580, 'n_words': 409, 'path': 'python/samples/2_connect.ipynb', 'repo_name': 'google/pikov', 'size': '4776'}