Skip to content

Instantly share code, notes, and snippets.

@MateusAquino
Created July 3, 2025 20:47
Show Gist options
  • Select an option

  • Save MateusAquino/fc1f5df74a9e0c15a79aa7213a340d98 to your computer and use it in GitHub Desktop.

Select an option

Save MateusAquino/fc1f5df74a9e0c15a79aa7213a340d98 to your computer and use it in GitHub Desktop.

Revisions

  1. MateusAquino created this gist Jul 3, 2025.
    155 changes: 155 additions & 0 deletions bad_apple.pde
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,155 @@
    import processing.serial.*;
    import static javax.swing.JOptionPane.*;
    import java.nio.file.*;
    import processing.data.JSONArray;
    import processing.data.JSONObject;

    Serial myPort;

    // Configurable params
    final int frameInterval = 33; // ~30 fps | NOTE: lower is faster
    final boolean WARN_RESERVOIR = true;
    final boolean UI_MODE = true;

    // Grid dimensions
    final int GRID_WIDTH = 14;
    final int GRID_HEIGHT = 8;
    final int WINDOW_WIDTH = 700;
    final int WINDOW_HEIGHT = 400;

    // Electrode & locked‑block state
    boolean[][] electrodes = new boolean[GRID_WIDTH][GRID_HEIGHT];

    // State
    ArrayList<boolean[][]> frames;
    int currentFrame = 0;
    int lastFrameTime = 0;


    void setup() {
    noLoop(); // pause draw while we init serial + UI

    // —— serial init ——
    String[] ports = Serial.list();
    if (ports == null || ports.length == 0) {
    showMessageDialog(null,
    "No serial ports found. Connect your device and retry.",
    "Serial Port Error", ERROR_MESSAGE);
    exit();
    }
    myPort = new Serial(this, ports[0], 115200);
    delay(100);

    // —— initial board fill ——
    initialTransmit();
    if (WARN_RESERVOIR) showMessageDialog(null,
    "This is an inverted grid. Fill the board (except the reservoir) with liquid *now*, then press OK.",
    "Initial setup", 2);

    // —— game init ——
    if (UI_MODE) windowResize(WINDOW_WIDTH, WINDOW_HEIGHT);
    frameRate(60);
    setupFrames();
    loop();
    }

    void setupFrames() {
    for (int x = 0; x < GRID_WIDTH; x++)
    for (int y = 0; y < GRID_HEIGHT; y++)
    electrodes[x][y] = true;

    transmit();
    loadFrames();
    }

    void loadFrames() {
    JSONArray raw = loadJSONArray("bad_apple_14x8.json");
    frames = new ArrayList<boolean[][]>();
    for (int f = 0; f < raw.size(); f++) {
    JSONArray frame = raw.getJSONArray(f);
    boolean[][] buf = new boolean[GRID_WIDTH][GRID_HEIGHT];
    for (int y = 0; y < GRID_HEIGHT; y++) {
    JSONArray row = frame.getJSONArray(y);
    for (int x = 0; x < GRID_WIDTH; x++) {
    buf[x][y] = row.getInt(x) == 1;
    }
    }
    frames.add(buf);
    }
    }

    void draw() {
    // drop piece
    if (millis() - lastFrameTime > frameInterval) {
    lastFrameTime = millis();
    displayFrame(frames.get(currentFrame));
    currentFrame = (currentFrame + 1) % frames.size();
    transmit();
    }

    if (UI_MODE) renderGrid();
    }

    void initialTransmit() {
    // fill all electrodes on
    for (int x = 0; x < GRID_WIDTH; x++)
    for (int y = 0; y < GRID_HEIGHT; y++)
    electrodes[x][y] = true;

    byte[] packet = new byte[32];
    packet[0] = 0;
    for (int x = 0; x < GRID_WIDTH; x++) {
    int b = 0;
    for (int y = 0; y < GRID_HEIGHT; y++)
    if (electrodes[x][y])
    b |= 1<<y;
    packet[x+1] = (byte)b;
    }
    packet[GRID_WIDTH+1] = 0;
    for (byte pb : packet) myPort.write(pb);
    }

    void transmit() {
    byte[] packet = new byte[32];
    packet[0] = (byte)0xFF;
    for (int x = 0; x < GRID_WIDTH; x++) {
    int b = 0;
    for (int y = 0; y < GRID_HEIGHT; y++)
    if (electrodes[x][y])
    b |= 1<<y;
    packet[x+1] = (byte)b;
    }
    packet[GRID_WIDTH+1] = (byte)0xFF;
    for (byte pb : packet) myPort.write(pb);
    }

    void renderGrid() {
    int cellW = width / GRID_WIDTH;
    int cellH = height / GRID_HEIGHT;
    stroke(80);
    for (int x = 0; x < GRID_WIDTH; x++) {
    for (int y = 0; y < GRID_HEIGHT; y++) {
    fill(electrodes[x][y] ? color(0,200,0) : 50);
    rect(x*cellW, y*cellH, cellW-2, cellH-2);
    }
    }
    }

    void displayFrame(boolean[][] f) {
    // copy into electrodes
    for (int x = 0; x < GRID_WIDTH; x++)
    for (int y = 0; y < GRID_HEIGHT; y++)
    electrodes[x][y] = f[x][y];
    }


    void dispose() {
    // clear electrodes on exit
    if (myPort != null) {
    byte[] clear = new byte[32];
    for (byte b: clear) myPort.write(b);
    delay(50);
    myPort.stop();
    }
    super.dispose();
    }
    1 change: 1 addition & 0 deletions bad_apple_14x8.json
    1 addition, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.