{"id":21314627,"url":"https://github.com/dor-sketch/tm","last_synced_at":"2025-03-15T21:22:42.776Z","repository":{"id":238130223,"uuid":"739042723","full_name":"Dor-sketch/TM","owner":"Dor-sketch","description":"A graphical application built using Python and the Pygame library to simulate the operation of Turing machines.","archived":false,"fork":false,"pushed_at":"2024-05-18T17:10:51.000Z","size":10855,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-22T10:23:03.714Z","etag":null,"topics":["computation","turing-machine","turing-machine-simulator"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Dor-sketch.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-01-04T16:32:14.000Z","updated_at":"2024-05-18T17:10:54.000Z","dependencies_parsed_at":"2024-05-17T18:06:48.067Z","dependency_job_id":null,"html_url":"https://github.com/Dor-sketch/TM","commit_stats":null,"previous_names":["dor-sketch/tm"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Dor-sketch%2FTM","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Dor-sketch%2FTM/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Dor-sketch%2FTM/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Dor-sketch%2FTM/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Dor-sketch","download_url":"https://codeload.github.com/Dor-sketch/TM/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243791136,"owners_count":20348423,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["computation","turing-machine","turing-machine-simulator"],"created_at":"2024-11-21T18:14:16.573Z","updated_at":"2025-03-15T21:22:42.704Z","avatar_url":"https://github.com/Dor-sketch.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# Turing Machine Simulator (AudioVisualSynth Integration)\n\n\u003cp align=\"center\"\u003e\n  \u003ctable align=\"center\"\u003e\n    \u003ctr\u003e\n      \u003ctd\u003e\u003cimg src=\"images/blue.png\" alt=\"Blue\" width=\"400\"/\u003e\u003c/td\u003e\n      \u003ctd\u003e\u003cimg src=\"images/red.png\" alt=\"Red\" width=\"400\"/\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003e\u003cimg src=\"images/purple.png\" alt=\"Purple\" width=\"400\"/\u003e\u003c/td\u003e\n      \u003ctd\u003e\u003cimg src=\"images/yellow.png\" alt=\"Yellow\" width=\"400\"/\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/table\u003e\n\u003c/p\u003e\n\n- [Overview](#overview)\n- [Features](#features)\n- [Installation](#installation)\n- [Usage](#usage)\n  - [Main Components](#main-components)\n  - [Running the Simulator](#running-the-simulator)\n- [Examples](#examples)\n  - [Non-Musical Turing Machine for Palindromic Languages and $0^{{3}^{n}}$ Languages](#non-musical-turing-machine-for-palindromic-languages-and-03n-languages)\n  - [Ed Sheeran - Photograph](#ed-sheeran---photograph)\n  - [Green Day - Boulevard of Broken Dreams](#green-day---boulevard-of-broken-dreams)\n  - [Hans Zimmer - Time (Inception Theme)](#hans-zimmer---time-inception-theme)\n  - [Owl City - Fireflies](#owl-city---fireflies)\n- [Code Explanation and Technical Details](#code-explanation-and-technical-details)\n  - [Program Structure](#program-structure)\n  - [MIDI to Turing Machine Conversion](#midi-to-turing-machine-conversion)\n  - [Drawing the Turing Machine GraphThe](#drawing-the-turing-machine-graphthe)\n- [Contributing](#contributing)\n- [License](#license)\n- [Acknowledgements](#acknowledgements)\n\n---\n\n## Overview\n\nThis project is a Turing Machine simulator with a graphical user interface (GUI) built using the `Pygame` library. The simulator visualizes the operation of a Turing Machine, displaying state transitions and tape movements, and incorporates a simple particle system for visual effects.\n\n## Features\n\n- **Turing Machine Simulation**: Simulate a Turing Machine with customizable states and transitions.\n- **Graphical Interface**: Use Pygame to display the Turing Machine tape, head, and state transitions.\n- **Particle System**: Add visual effects to the simulation.\n- **MIDI Integration**: Converts MIDI files into Turing Machine transitions.\n- **CSV Loading**: Load Turing Machine transitions from CSV files.\n- **Dynamic Graph Theme**: Changes the theme of the state transition graph based on the current time.\n\n## Installation\n\n1. **Clone the repository**:\n\n    ```sh\n    git clone https://github.com/Dor-sketch/TM.git\n    cd TM\n    ```\n\n2. **Install dependencies**:\n\n    ```sh\n    pip install -r requirements.txt\n    ```\n\n    To generate notes, clone the  [AudioVisualSynth](https://github.com/Dor-sketch/AudioVisualSynth) repository and follow the instructions in its README.\n\n    Alternatively, you can use `music.py` from the `AudioVisualSynth` repository to generate notes.\n\n3. **Run the simulator**:\n\n    ```sh\n    python turing_machine_app.py\n    ```\n\n---\n\n## Usage\n\n### Main Components\n\n- **TuringMachineApp**: This is the main application class that initializes the GUI and handles events.\n- **ParticleSystem**: \"This manages and updates particles for visual effects.\n- **TuringGraph**: Draws the state transition graph of the Turing Machine.\n- **midi_to_tm**: Converts MIDI files into Turing Machine transitions.\n\n### Running the Simulator\n\n1. **Launch the simulator**:\n\n    ```sh\n    python turing_machine_app.py\n    ```\n\n2. **Load Transitions**: Use the `Load` button to load transitions from a CSV or MIDI file.\n\n3. **Reset the Machine**: Use the `Reset` button to reset the Turing Machine to its initial state.\n\n4. **Input**: Enter an input string for the Turing Machine in the provided text box.\n\n5. **Run the Simulation**: Use keyboard controls (`Arrow Keys`) to step through the simulation.\n\n## Examples\n\nTo see the Turing Machine in action, load an example MIDI or CSV file, and start the simulation. Observe how the Turing Machine processes the input and moves through its states while particles provide visual feedback. Here are some examples of the Turing Machine in action. I do not own the rights to the music used in these examples. The MIDI files in the `mids/` directory are my own compositions.\n\n### Non-Musical Turing Machine for Palindromic Languages and $0^{{3}^{n}}$ Languages\n\n[Watch the video here](https://github.com/Dor-sketch/TM/assets/138825033/c7e489f6-9cc6-482a-b8ba-52ea8af576f6)\n\n### Ed Sheeran - Photograph\n\n[Watch here](https://github.com/Dor-sketch/TM/assets/138825033/f9c2e540-ec35-44a8-b9ef-0a51ac3b74f0)\n\n### Green Day - Boulevard of Broken Dreams\n\n[Watch here](https://github.com/Dor-sketch/TM/assets/138825033/ff996125-f73e-4299-ac27-0b599540e7d5)\n\n### Hans Zimmer - Time (Inception Theme)\n\n[Watch here](https://github.com/Dor-sketch/TM/assets/138825033/990c8c49-ec03-4d21-81f4-0b32ad326142)\n\n### Owl City - Fireflies\n\n[Watch here](https://github.com/Dor-sketch/TM/assets/138825033/20a33769-e3d8-459b-ae3d-9bfd945b7f65)\n\n---\n\n## Code Explanation and Technical Details\n\n### Program Structure\n\n- `turing_machine_app.py`: Main application code.\n- `particle_system.py`: Contains the particle system and particle classes.\n- `turing_machine.py`: Defines the Turing Machine class.\n- `turing_machine_graph.py`: Manages the state transition graph drawing.\n- `miditm.py`: Converts MIDI files to Turing Machine transitions.\n- `utils.py`: Utility functions for loading transitions from CSV and generating colors.\n- `config.py`: Configuration constants for the application.\n- `input_string.txt`: Default input string for the Turing Machine.\n- `mids/`: Directory containing example MIDI files for testing.\n- `csvs/`: Directory containing example CSV files for testing.\n- `images/`: Directory containing images for the README.\n\n### MIDI to Turing Machine Conversion\n\nThe `midi_to_tm` function in `miditm.py` converts a MIDI file to a Turing Machine. It processes `note_on` and `note_off` messages to create states and transitions for the Turing Machine.\n\n```python\nimport mido\nfrom turing_machine import TuringMachine, DEFAULT_INPUT_SYMBOLS, DEFAULT_TAPE_SYMBOLS\n\ndef initialize_variables():\n    states = set(['q1', 'q_accept', 'q_reject'])\n    transitions = {'q1': {}, 'q_accept': {}, 'q_reject': {}}\n    first_note = None\n    last_note = None\n    input_string = []\n    note_on_time = {}\n    tempo = mido.bpm2tempo(60)\n    prev_note = None\n    chord = []\n    prev_time = 0\n    current_time = 0\n    return states, transitions, first_note, last_note, input_string, note_on_time, tempo, prev_note, chord, prev_time, current_time\n\ndef process_note_on(msg, filter, current_time, prev_time, chord, input_string, first_note, last_note, states, transitions, prev_note, note_on_time):\n    if filter and (msg.note \u003c 50 or msg.note \u003e 81):\n        return prev_time, chord, first_note, last_note, states, transitions, prev_note\n    note_str = str(msg.note)\n    note_on_time[note_str] = current_time\n    if prev_time != current_time:\n        if chord:\n            input_string.append(','.join(chord))\n            chord = []\n        prev_time = current_time\n    chord.append(note_str)\n    if first_note is None:\n        first_note = note_str\n    last_note = note_str\n    states.add(note_str)\n    if note_str not in transitions:\n        transitions[note_str] = {}\n    if prev_note is not None:\n        transitions[prev_note][note_str] = (note_str, 'X', 'R')\n    prev_note = note_str\n    return prev_time, chord, first_note, last_note, states, transitions, prev_note\n\ndef process_note_off(msg, current_time, note_on_time):\n    note_str = str(msg.note)\n    if note_str in note_on_time:\n        del note_on_time[note_str]\n\ndef midi_to_tm(midi_file, filter=False):\n    mid = mido.MidiFile(midi_file)\n    states, transitions, first_note, last_note, input_string, note_on_time, tempo, prev_note, chord, prev_time, current_time = initialize_variables()\n    for msg in mid:\n        if msg.type == 'set_tempo':\n            tempo = msg.tempo\n        current_time += mido.tick2second(msg.time, mid.ticks_per_beat, tempo)\n        if msg.type == 'note_on':\n            prev_time, chord, first_note, last_note, states, transitions, prev_note = process_note_on(msg, filter, current_time, prev_time, chord, input_string, first_note, last_note, states, transitions, prev_note, note_on_time)\n        if msg.type == 'note_off':\n            process_note_off(msg, current_time, note_on_time)\n    if chord:\n        input_string.append(','.join(chord))\n    transitions['q1'][first_note] = (first_note, 'X', 'R')\n    transitions[last_note]['q_accept'] = ('q_accept', 'X', 'R')\n    input_string = ' '.join(input_string)\n    with open('input_string.txt', 'w') as f:\n        f.write(input_string)\n    return TuringMachine(states, DEFAULT_INPUT_SYMBOLS, DEFAULT_TAPE_SYMBOLS, transitions)\n```\n\n### Drawing the Turing Machine GraphThe\n\n`TuringGraph` class in `turing_machine_graph.py` is responsible for drawing the Turing Machine's state transition graph using Pygame. It includes methods to set the theme, draw states and transitions, and calculate node positions.\n\n```python\nimport math\nimport random\nimport time\nfrom collections import deque\nimport pygame\nimport pygame.gfxdraw\nfrom PIL import Image, ImageDraw\nfrom config import SCREEN_WIDTH, SCREEN_HEIGHT, STATE_COLOR, TEXT_COLOR, LINES_COLOR, font, palettes\n\nclass TuringGraph:\n    def __init__(self, states, transitions):\n        self.line_color = LINES_COLOR\n        self.circle_color = STATE_COLOR\n        self.text_color = TEXT_COLOR\n        self.graph = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT))\n        self.states_list = list(states)\n        self.transitions = transitions\n        self.positions = {state: (random.uniform(0, SCREEN_WIDTH), random.uniform(0, SCREEN_HEIGHT)) for state in self.states_list}\n        self.init_positions()\n        self.generate_graph()\n\n    def set_theme(self):\n        t = time.time()\n        palette_index = int(t / 10) % len(palettes)\n        color_index1 = int(t) % len(palettes[palette_index])\n        color_index2 = (color_index1 + 1) % len(palettes[palette_index])\n        color1 = palettes[palette_index][color_index1]\n        color2 = palettes[palette_index][color_index2]\n        gradient = Image.new('RGB', self.graph.get_size())\n        draw = ImageDraw.Draw(gradient)\n        for i in range(self.graph.get_width()):\n            r = color1[0] * (1 - i/self.graph.get_width()) + color2[0] * (i/self.graph.get_width())\n            g = color1[1] * (1 - i/self.graph.get_width()) + color2[1] * (i/self.graph.get_width())\n            b = color1[2] * (1 - i/self.graph.get_width()) + color2[2] * (i/self.graph.get_width())\n            draw.line([(i, 0), (i, self.graph.get_height())], fill=(int(r), int(g), int(b)))\n        gradient_pygame = pygame.image.fromstring(gradient.tobytes(), gradient.size, gradient.mode)\n        self.graph.blit(gradient_pygame, (0, 0))\n        self.line_color = (255 - color2[0], 255 - color2[1], 255 - color2[2])\n        self.circle_color = ((255 + color2[0]) // 2, (255 + color2[1]) // 2, (255 + color2[2]) // 2)\n\n    def generate_graph(self):\n        self.set_theme()\n        drawn = {}\n        self.draw_transitions(drawn)\n        self.draw_states()\n\n    def draw_transitions(self, drawn):\n        for state, transitions in self.transitions.items():\n            for symbol, (next_state, write_symbol, move_direction) in transitions.items():\n                if state == next_state:\n                    self.draw_loop(state, drawn, symbol, write_symbol, move_direction)\n                else:\n                    self.draw_line(state, next_state, symbol, write_symbol, move_direction)\n\n    def draw_loop(self, state, drawn, symbol, write_symbol, move_direction):\n        if state in drawn:\n            x, y = drawn[state]\n            self.blit_text(x, y - 20, symbol, write_symbol, move_direction)\n        else:\n            x, y = self.positions[state]\n            self.draw_arc(x, y)\n            self.blit_text(x + 10, y - 50, symbol, write_symbol, move_direction)\n            drawn[state] = (x, y)\n\n    def draw_arc(self, x, y):\n        rect = pygame.Rect(x, y - 30, 30, 30)\n        start_angle = math.pi / 2\n        stop_angle = 2.5 * math.pi\n        pygame.draw.arc(self.graph, self.text_color, rect, start_angle, stop_angle, width=2)\n\n    def draw_line(self, state, next_state, symbol, write_symbol, move_direction):\n        start_x, start_y = self.positions[state]\n        end_x, end_y = self.positions[next_state]\n        start_x += 20\n        end_x -= 20\n        pygame.draw.aaline(self.graph, self.line_color, (start_x, start_y), (end_x, end_y))\n        self.draw_arrow(start_x, start_y, end_x, end_y)\n        self.blit_text((start_x + end_x) / 2, (start_y + end_y) / 2, symbol, write_symbol, move_direction)\n\n    def draw_arrow(self, start_x, start_y, end_x, end_y):\n        dx = end_x - start_x\n        dy = end_y - start_y\n        angle = math.atan2(dy, dx)\n        ARROW_LENGTH = 10\n        ARROW_dx = ARROW_LENGTH * math.cos(angle)\n        ARROW_dy = ARROW_LENGTH * math.sin(angle)\n        ARROW_point1 = (end_x - ARROW_dx + ARROW_dy / 2, end_y - ARROW_dy - ARROW_dx / 2)\n        ARROW_point2 = (end_x - ARROW_dx - ARROW_dy / 2, end_y - ARROW_dy + ARROW_dx / 2)\n        pygame.draw.polygon(self.graph, self.line_color, [(end_x, end_y), ARROW_point1, ARROW_point2])\n\n    def blit_text(self, x, y, symbol, write_symbol, move_direction):\n        text_surface = font.render(f\"{symbol}{ARROW}{write_symbol},{move_direction}\", True, self.text_color)\n        text_surface.set_alpha(200)\n        text_surface.set_colorkey((0, 255, 0))\n        self.graph.blit(text_surface, (x, y))\n\n    def draw_states(self):\n        for state in self.states_list:\n            x, y = self.positions[state]\n            self.draw_3d_circle(self.graph, x, y, 20, self.circle_color, text=state, text_color=self.text_color)\n\n    def draw_3d_circle(self, surface, x, y, radius, color, text=None, text_color=(255, 255, 255)):\n        x, y, radius = int(x), int(y), int(radius)\n        shadow_color = (0, 0, 0, 50)\n        pygame.gfxdraw.filled_circle(surface, x + radius // 4, y + radius // 4, radius, shadow_color)\n        for i in range(radius):\n            alpha = round(255 * (i / radius))\n            pygame.gfxdraw.filled_circle(surface, x, y, radius - i, (*color, alpha))\n        if text is not None:\n            font = pygame.font.Font(None, 24)\n            text_surface = font.render(text, True, text_color)\n            text_rect = text_surface.get_rect(center=(x, y))\n            surface.blit(text_surface, text_rect)\n\n    def init_positions(self):\n        num_layers, max_nodes_in_layer = self.calculate_layers_and_nodes()\n        SPACING_X, SPACING_Y, MARGIN_X, MARGIN_Y = self.calculate_spacing(num_layers, max_nodes_in_layer)\n        visited, queue, layer_nodes = self.initialize_queue()\n        while queue:\n            state, layer = queue.popleft()\n            if not visited[state]:\n                visited[state] = True\n                self.update_positions(state, layer, SPACING_X, SPACING_Y, MARGIN_X, layer_nodes)\n                for next_state in self.transitions[state].values():\n                    queue.append((next_state[0], layer + 1))\n\n    def calculate_layers_and_nodes(self):\n        self.start_state = 'q1'\n        visited = {state: False for state in self.states_list}\n        queue = deque([(self.start_state, 0)])\n        layer_nodes = {}\n        max_layer = 0\n        max_nodes_in_layer = 0\n        while queue:\n            state, layer = queue.popleft()\n            if not visited[state]:\n                visited[state] = True\n                layer_nodes.setdefault(layer, []).append(state)\n                max_layer = max(max_layer, layer)\n                max_nodes_in_layer = max(max_nodes_in_layer, len(layer_nodes[layer]))\n                for next_state in self.transitions[state].values():\n                    queue.append((next_state[0], layer + 1))\n        return max_layer + 1, max_nodes_in_layer\n\n    def calculate_spacing(self, num_layers, max_nodes_in_layer):\n        SPACING_X = SCREEN_WIDTH // (num_layers + 1)\n        SPACING_Y = SCREEN_HEIGHT // (max_nodes_in_layer + 1)\n        MARGIN_X = SPACING_X\n        MARGIN_Y = SPACING_Y * 2\n        return SPACING_X, SPACING_Y, MARGIN_X, MARGIN_Y\n\n    def initialize_queue(self):\n        self.start_state = 'q1'\n        visited = {state: False for state in self.states_list}\n        queue = deque([(self.start_state, 0)])\n        layer_nodes = {}\n        return visited, queue, layer_nodes\n\n    def update_positions(self, state, layer, SPACING_X, SPACING_Y, MARGIN_X, layer_nodes):\n        x = MARGIN_X + layer * SPACING_X\n        num_nodes_in_layer = len(layer_nodes.get(layer, []))\n        layer_height = SPACING_Y * (num_nodes_in_layer - 1)\n        start_y = (SCREEN_HEIGHT - layer_height) // 2\n        for i, state in enumerate(layer_nodes.get(layer, [])):\n            y = start_y + i * SPACING_Y\n            self.positions[state] = (x, y)\n        layer_nodes.setdefault(layer, []).append(state)\n```\n\n## Contributing\n\nContributions are welcome! Please fork the repository, make your changes, and create a pull request. Here are some areas for potential contributions:\n\n1. **Multiple Tape Support**: The current implementation reads symbols that represent multiple notes to simulate playing chords. This is a simplification and doesn't fully leverage the potential of a multitape Turing Machine. Implementing multiple tapes, each representing a note, would allow the Turing Machine to play chords and more complex music. Note that while the current implementation visually represents multiple notes, the state transitions are still based on a single tape.\n\n2. **Tempo and Time States**: At present, a text file is used to dictate the duration of each note due to the limitations of the single tape model. A more integrated approach would be to incorporate note duration within the transition functions. This could be achieved by adding states that represent tempo and time, or by implementing a multitape Turing Machine where one tape represents the notes and another represents the note duration.\n\n3. **AI-Based Sound File Parsing**: Currently, a MIDI file is used to generate the Turing Machine transitions. An exciting enhancement would be to implement an AI model that can parse any sound file and generate the Turing Machine transitions. This would allow the Turing Machine to play a wider variety of sound files, not just MIDI files.\n\n## License\n\nThis project is licensed under the MIT License. Refer to the [LICENSE](LICENSE) file for details.\n\n## Acknowledgements\n\n- Thanks to the `Pygame` library for providing the foundation for the GUI.\n- Thanks to the `Pygame GUI` extension for handling UI elements.\n- Thanks to the `mido` library for aiding in MIDI file processing.\n- Thanks to the `PIL` library for image processing.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdor-sketch%2Ftm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdor-sketch%2Ftm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdor-sketch%2Ftm/lists"}