Mirko Pavleski
Published © GPL3+

DIY Arduino Tug of War Game on Homemade 8x8 Led Matrix

Tug of War is an old game where two players pull on a rope, one player winning by pulling the rope far enough to their own side.

IntermediateFull instructions provided2,066
DIY Arduino Tug of War Game on Homemade 8x8 Led Matrix

Things used in this project

Hardware components

Arduino Nano R3
Arduino Nano R3
×1
LED (generic)
LED (generic)
×64
Resistor 221 ohm
Resistor 221 ohm
×8
Resistor 10k ohm
Resistor 10k ohm
×2
Pushbutton Switch, Momentary
Pushbutton Switch, Momentary
×2
Buzzer
Buzzer
×1

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free

Story

Read more

Schematics

Schematic diagram

Code

Arduino code

C/C++
//
// Project 1 - Tug-Of-War
//
// Code by Andrew R McHugh
// Gadgets, Carnegie Mellon Univeristy, Hudson


/*
   Game Mechanics
   --------------

   Two players mash their buttons as fast as possible
   This creates a tug-of-war with the LED "rope"
   When one player gets a point, the board flashes and points to them with an arrow
   When one player wins (first to three points), the board flashes and shows the number of the winning player (left = 1, right = 2)
   Music plays in the background, randomly generated from the E minor
   Music speed increases as either player gets closer to winning a point

*/

// Include libraries
// Letters for the displayed patterns
// Pitches for the music notes
#include "letters.h"
#include "pitches.h"

// Set global variables
// Devmode turns on Serial.print() comments
bool DEVMODE = false;
// Mute mutes the speaker
bool MUTE = false;

// STATE is a variable that controls the global state of the game
enum States {
  startup,
  play,
  playerWin,
  win,
  postGame,
  score
};
States STATE;

// Setup the pins, mostly in arrays
const int ledPinsRows[] = {4, 3, 0, A0, A1, A2, A3, A4};
const int ledPinsCols[] = {12, 11, 10, 9, 8, 7, 6, 5};//0
const int btnPins[] = {A5, 2};
const int musicPin = 13;

// Define the musical scale to randomly pull from
int music[] {
  // E Minor
  NOTE_E3, NOTE_G3, NOTE_A3, NOTE_B4, NOTE_D4, NOTE_E4, NOTE_G4, NOTE_A4
};
// Number of notes in array
const int noteCount = 8;
// Initializing the position of the current note
int notePosition = 0;

// Durration of notes before speed adjustments, 4 = a quarter note
int noteDurations[] = {
  4, 4, 4, 4, 4, 4, 4, 4
};
// Initialize other music variables
int noteDuration; //defined later in the code
int musicSpeed = 1; //1 is normal, 2 is kinda-fast, three is fast

// Defined button labels for ease of use and increased readability
const int LEFT = 0;
const int RIGHT = 1;

// Set the initial states and last states
int btnStates[] = {HIGH, HIGH}; //{left, right}
int btnStatesLast[] = {LOW, LOW};

// Set the initial player scores
int playerScore[] = {1, 1}; // round scores, to be updated per round, based on the math, they start at 1
int playerScoreInit[] = {1, 1}; // defined initial round scores
int playerScoreMaster[] = {0, 0}; // the master score that can change the overall game state

// Initialize the who won last and who the game winner is
int playerWinLast;
int playerWinner;

// Initialize the total score and win location
int totalScore;
int winLocation; // defines the location of the "rope" on the led matrix

// Initialize time variables, definitions follow in code
unsigned long timeStart;
unsigned long timeLimit;
unsigned long timeMeter;
unsigned long masterClock;
unsigned long lastNotePlayedClock;

// Initialize the playArray which gets drawn on the board
int playArray[8][8];

//
// Updates button states. Sets current-stored to
// last then reads current states
//
void updateBtnStates() {
  btnStatesLast[LEFT] = btnStates[LEFT];
  btnStatesLast[RIGHT] = btnStates[RIGHT];

  btnStates[LEFT] = digitalRead(btnPins[LEFT]);
  btnStates[RIGHT] = digitalRead(btnPins[RIGHT]);
}

// Draws a given array onto the dot matrix. If
// passed an optional variable "inverse", the
// function draws the inverse of the array.
void draw(const int dotArray[8][8], bool inverse = false) {
  
  // Initialize a this variable
  int thisLED = 0;

  // Loop that draws the given array onto the matrix
  for (int thisRow = 0; thisRow < 8; thisRow++) {
    digitalWrite(ledPinsRows[thisRow], LOW);
    for (int thisCol = 0; thisCol < 8; thisCol++) {

      thisLED = dotArray[thisRow][thisCol];

      // checks inverse variable to either make 1s or 0s the drawn dot
      if (inverse == false) {
        if (thisLED == 1) {
          digitalWrite(ledPinsCols[thisCol], HIGH);
        }
      } else if (inverse == true) {
        if (thisLED == 0) {
          digitalWrite(ledPinsCols[thisCol], HIGH);
        }
      }

      // Magic sauce
      digitalWrite(ledPinsCols[thisCol], LOW);
    }
    digitalWrite(ledPinsRows[thisRow], HIGH);
  }

}

// draw() for a specified amount of time (without interupt)
void drawFor(const int dotArray[8][8], int mil, bool inverse = false) {

  // Set time and given duration
  timeStart = millis();
  timeLimit = timeStart + mil;
  timeMeter = timeStart;

  // While the counted time is less than the desired limit, keep drawing the array
  while (timeMeter < timeLimit) {
    // Draw the array
    draw(dotArray, inverse);
    // Update the counted time
    timeMeter = millis();
  }
}

// Specific "draw" for setting the matrix on
void setMatrixOn() {
  for (int thisCol = 0; thisCol < 8; thisCol++) {
    digitalWrite(ledPinsCols[thisCol], HIGH);
  }
  for (int thisRow = 0; thisRow < 8; thisRow++) {
    digitalWrite(ledPinsRows[thisRow], LOW);
  }
}

// Specific "draw" for setting the matrix off
void setMatrixOff() {
  for (int thisCol = 0; thisCol < 8; thisCol++) {
    digitalWrite(ledPinsCols[thisCol], LOW);
  }
  for (int thisRow = 0; thisRow < 8; thisRow++) {
    digitalWrite(ledPinsRows[thisRow], HIGH);
  }
}

// A set of development tests, controlled by case switch
// Not used for production
void testMatrix(int test) {
  switch (test) {
    case 1:
      for (int thisCol = 0; thisCol < 8; thisCol++) {
        for (int thisRow = 0; thisRow < 8; thisRow++) {
          digitalWrite(ledPinsRows[thisRow], LOW);
          digitalWrite(ledPinsCols[thisCol], HIGH);
          delay(20);
          digitalWrite(ledPinsRows[thisRow], HIGH);
          digitalWrite(ledPinsCols[thisCol], LOW);
        }
      }
      break;
    case 2:
      draw(TEST);
      break;
    default:
      setMatrixOn();
      delay(100);
      setMatrixOff();
  }
}

// Turn just one LED on
void ledOn(int row, int col, bool setMatrixOffVar = false) {
  // Check to see if we should start by turning the whole matrix off
  if (setMatrixOffVar == true) {
    setMatrixOff();
  }
  digitalWrite(ledPinsCols[col], HIGH);
  digitalWrite(ledPinsRows[row], LOW);
}

// Turn just one LED off
void ledOff(int row, int col, bool setMatrixOnVar = false) {
  // Check to see if we should start by turning the whole matrix on
  if (setMatrixOnVar == true) {
    setMatrixOn();
  }
  digitalWrite(ledPinsCols[col], LOW);
  digitalWrite(ledPinsRows[row], HIGH);
}
// Update the scores and redraw matrix accordingly
void updateScores() {
  // See who is pressing their button
  // Only adds one point per press, holding doesn't work here
  for (int side = 0; side < 2; side++) {
    if ((btnStates[side] != btnStatesLast[side]) && (btnStates[side] == HIGH)) {
      playerScore[side]++;
      if (DEVMODE == true) {
        Serial.print("Player Score ");
        Serial.print(side);
        Serial.print(playerScore[side]);
        Serial.println();
        delay(10); //edit
      }
    }
  }
  // Can update the buttons states now
  updateBtnStates();

  // Read scores
  // Draw scores based on "winningness"
  // If score is high enough, change state to win

  // The numbers in this if statement are "magic numbers" I played with them and they
  // seem to give an enjoyable experience with variability.
  if ((playerScore[LEFT] > 15) || (playerScore[RIGHT] > 15)) {
    // Because their score gets mapped to a location on the display, winning requires
    // a higher percentage, not pure number. I thus decrement the score (which changes
    // the mapping behavior and constrain this adjustment to keep some stability.
    playerScore[LEFT]  = constrain(playerScore[LEFT]  - 4, 0, playerScore[LEFT] - 4);
    playerScore[RIGHT] = constrain(playerScore[RIGHT] - 4, 0, playerScore[RIGHT] - 4);
  }
  totalScore = playerScore[LEFT] + playerScore[RIGHT];
  // Maps the scores to a usable location. -1 and 9 are the win states
  winLocation = map(playerScore[RIGHT], 0, totalScore, -1, 9);
  
  // Update the music speed based on their "winningness" location on the matrix
  updateMusicSpeed(winLocation);

  // Dev function
  if (DEVMODE == true) {
    Serial.println(winLocation);
  }
  // Win state
  if (winLocation == -1) {
    playerScoreMaster[LEFT] += 1;
    playerWinLast = LEFT;
    STATE = playerWin;
  }
  // Win state
  else if (winLocation == 9) {
    playerScoreMaster[RIGHT] += 1;
    playerWinLast = RIGHT;
    STATE = playerWin;
  }
  // If you don't win, draw the matrix again
  else {
    for (int j = 0; j < 8; j++) {
      for (int i = 0; i < 8; i++) {
        // Selectively draw two lines for the pure center of the matrix
        if ((winLocation == 4) && (j == winLocation)) {
          playArray[i][j] = 1;
          playArray[i][j - 1] = 1;
        }
        // Otherwise draw a single line on either side of center
        else if ((winLocation < 4) && (j == winLocation)) {
          playArray[i][j] = 1;
        }
        else if ((winLocation > 4) && (j == winLocation - 1)) {
          playArray[i][j] = 1;
        }
        else {
          playArray[i][j] = 0;
        }
      }
    }
    // Now that you've prepared the array, draw it
    draw(playArray);
  }
}

// When a player wins a round...
void playerWinSingle() {
  // Flashes the board and points to who won the round
  drawFor(OFF, 300);
  drawFor(ON, 300);
  drawFor(OFF, 300);
  drawFor(ON, 300);
  if (playerWinLast == LEFT) {
    drawFor(ARROW_LEFT, 1000);
  }
  else if (playerWinLast == RIGHT) {
    drawFor(ARROW_RIGHT, 1000);
  }

  // Dev function
  if(DEVMODE == true){
    Serial.print("Player win last ");
    Serial.println(playerWinLast);
    Serial.print("Player winner ");
    Serial.println(playerWinner);
    Serial.print("Player score master left ");
    Serial.println(playerScoreMaster[LEFT]);
    Serial.print("Player score master right ");
    Serial.println(playerScoreMaster[RIGHT]);
  }

  // If one of the players has reached three points, set the overall game state to win
  if ((playerScoreMaster[LEFT] == 3) || (playerScoreMaster[RIGHT] == 3)) {
    if (playerScoreMaster[LEFT] == 3) {
      playerWinner = LEFT;
    }
    else if ((playerScoreMaster[RIGHT] == 3)) {
      playerWinner = RIGHT;
    }
    STATE = win;
  }
  // But if no one wins, set the game state back to play and reset player round scores
  else {
    STATE = play;
    playerScore[LEFT] = playerScoreInit[LEFT];
    playerScore[RIGHT] = playerScoreInit[RIGHT];
  }
}

// If a player wins the entire game...
void playerWinGame() {

  // Flash the board
  drawFor(OFF, 300);
  drawFor(ON, 300);
  drawFor(OFF, 300);
  drawFor(ON, 300);

  // Draw their number if left
  if (playerWinner == LEFT) {
    drawFor(PLAYER_1, 1000);
    drawFor(PLAYER_1,  500, true);
    drawFor(PLAYER_1, 1000);
    drawFor(PLAYER_1,  500, true);
    drawFor(PLAYER_1, 1000);
    drawFor(PLAYER_1,  500, true);
    drawFor(PLAYER_1, 1000);
  }
  // or if right
  else if (playerWinner == RIGHT) {
    drawFor(PLAYER_2, 1000);
    drawFor(PLAYER_2,  500, true);
    drawFor(PLAYER_2, 1000);
    drawFor(PLAYER_2,  500, true);
    drawFor(PLAYER_2, 1000);
    drawFor(PLAYER_2,  500, true);
    drawFor(PLAYER_2, 1000);
  }
}

// Sometimes you have to stop the love
// Shorthand to stop playing the music
void stopMusic() {
  noTone(musicPin);
}

// A small function that updates the music speed based on the
// level of "winningness". More win equals more fast.
void updateMusicSpeed(int winLocation) {
  int absWinLocation = abs(winLocation - 4);
  if (absWinLocation < 2) {
    musicSpeed = 1; // normal speed
  }
  else if (absWinLocation < 3) {
    musicSpeed = 2; // kinda-fast
  }
  else {
    musicSpeed = 3; // real-fast
  }
}

// Function that plays the music
// Optionally, it can run "asyncronously"
// Syncronous function adapted from Melody example
void playMusic(int toneSpeed, bool async = false) {
  // No music if you've muted
  if (MUTE == true) {
    stopMusic();
  }
  else {
    // If you're doing asyncronous playing
    if (async == true) {
      /*
         Get masterclock time
         If masterclock time > lastnoteplayed + noteduration,
           play next note
         Otherwise keep playing the current note
      */

      masterClock = millis();
      noteDuration = (1000 / noteDurations[notePosition]) / toneSpeed;

      if (masterClock > lastNotePlayedClock + noteDuration) {
        if (music[notePosition] == 0) {
          noTone(musicPin);
        }
        else {
          tone(musicPin, music[notePosition]);
        }
        // Choose a random note to play
        notePosition = random(0, 7) % noteCount;
        lastNotePlayedClock = millis();
      }

    }
    // this is basically exactly what was in the Melody example
    else if (async == false) {
      // iterate over the notes of the melody:
      for (int thisNote = 0; thisNote < noteCount; thisNote++) {

        // to calculate the note duration, take one second
        // divided by the note type.
        //e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.
        noteDuration = (1000 / noteDurations[thisNote]) / toneSpeed;
        tone(musicPin, music[thisNote], noteDuration);

        // to distinguish the notes, set a minimum time between them.
        // the note's duration + 30% seems to work well:
        double pauseBetweenNotes = noteDuration * 1;
        delay(pauseBetweenNotes);
        // stop the tone playing:
        noTone(musicPin);
      }
    }
  }
}

// To restart the game, you can hold both buttons down after someone wins
void restart() {
  updateBtnStates();
  if ((btnStates[LEFT] == HIGH) && (btnStates[LEFT] == HIGH)) {
    playerScore[LEFT] = playerScoreInit[LEFT];
    playerScore[RIGHT] = playerScoreInit[RIGHT];
    playerScoreMaster[LEFT] = 0;
    playerScoreMaster[RIGHT] = 0;
    STATE = startup;
  }

}
// The initial draw function, "loading"
void drawStartup() {
  for (int i = 0; i < 8; i++) {
    for (int j = 0; j < 8; j++) {
      ledOn(i, j, true);
      delay(25);
    }
  }

  playMusic(1);
  playMusic(2);
  playMusic(1);


  STATE = play;
}

// The setup function. It sets up the environment on load.
void setup() {
  masterClock = millis();

  if (DEVMODE == true) {
    Serial.begin(9600);
  }

  // Set LEDs to output
  for (int thisLED = 0; thisLED < 8; thisLED++) {
    pinMode(ledPinsCols[thisLED], OUTPUT);
  };
  for (int thisLED = 0; thisLED < 8; thisLED++) {
    pinMode(ledPinsRows[thisLED], OUTPUT);
  };

  pinMode(musicPin, OUTPUT);

  // Set btns to input
  pinMode(btnPins[LEFT], INPUT);
  pinMode(btnPins[RIGHT], INPUT);

  if (DEVMODE == true) {
    // flash the board to make sure everything is plugged in
    setMatrixOn();
    delay(300);
    setMatrixOff();
    Serial.println("Setup complete");
  }

  // Set game state
  STATE = startup;
}



void loop() {
  // Master switch that controls what happens in which game state
  switch (STATE) {
    case startup:
      drawStartup();
      break;
    case play:
      updateScores();
      playMusic(musicSpeed, true);
      break;
    case playerWin:
      stopMusic();
      playerWinSingle();
      break;
    case win:
      stopMusic();
      playerWinGame();
      restart();
      break;
  }

}

Pitches.h

C/C++
/*************************************************
 * Public Constants
 * 
 * This note table was originally written by Brett
 * Hagman, on whose work the tone() command was
 * based.
 * 
 *************************************************/

#define NOTE_B0  31
#define NOTE_C1  33
#define NOTE_CS1 35
#define NOTE_D1  37
#define NOTE_DS1 39
#define NOTE_E1  41
#define NOTE_F1  44
#define NOTE_FS1 46
#define NOTE_G1  49
#define NOTE_GS1 52
#define NOTE_A1  55
#define NOTE_AS1 58
#define NOTE_B1  62
#define NOTE_C2  65
#define NOTE_CS2 69
#define NOTE_D2  73
#define NOTE_DS2 78
#define NOTE_E2  82
#define NOTE_F2  87
#define NOTE_FS2 93
#define NOTE_G2  98
#define NOTE_GS2 104
#define NOTE_A2  110
#define NOTE_AS2 117
#define NOTE_B2  123
#define NOTE_C3  131
#define NOTE_CS3 139
#define NOTE_D3  147
#define NOTE_DS3 156
#define NOTE_E3  165
#define NOTE_F3  175
#define NOTE_FS3 185
#define NOTE_G3  196
#define NOTE_GS3 208
#define NOTE_A3  220
#define NOTE_AS3 233
#define NOTE_B3  247
#define NOTE_C4  262
#define NOTE_CS4 277
#define NOTE_D4  294
#define NOTE_DS4 311
#define NOTE_E4  330
#define NOTE_F4  349
#define NOTE_FS4 370
#define NOTE_G4  392
#define NOTE_GS4 415
#define NOTE_A4  440
#define NOTE_AS4 466
#define NOTE_B4  494
#define NOTE_C5  523
#define NOTE_CS5 554
#define NOTE_D5  587
#define NOTE_DS5 622
#define NOTE_E5  659
#define NOTE_F5  698
#define NOTE_FS5 740
#define NOTE_G5  784
#define NOTE_GS5 831
#define NOTE_A5  880
#define NOTE_AS5 932
#define NOTE_B5  988
#define NOTE_C6  1047
#define NOTE_CS6 1109
#define NOTE_D6  1175
#define NOTE_DS6 1245
#define NOTE_E6  1319
#define NOTE_F6  1397
#define NOTE_FS6 1480
#define NOTE_G6  1568
#define NOTE_GS6 1661
#define NOTE_A6  1760
#define NOTE_AS6 1865
#define NOTE_B6  1976
#define NOTE_C7  2093
#define NOTE_CS7 2217
#define NOTE_D7  2349
#define NOTE_DS7 2489
#define NOTE_E7  2637
#define NOTE_F7  2794
#define NOTE_FS7 2960
#define NOTE_G7  3136
#define NOTE_GS7 3322
#define NOTE_A7  3520
#define NOTE_AS7 3729
#define NOTE_B7  3951
#define NOTE_C8  4186
#define NOTE_CS8 4435
#define NOTE_D8  4699
#define NOTE_DS8 4978

Letters.h

C/C++
const int OFF[8][8] = {
  {0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0}
};
const int ON[8][8] = {
  {1, 1, 1, 1, 1, 1, 1, 1},
  {1, 1, 1, 1, 1, 1, 1, 1},
  {1, 1, 1, 1, 1, 1, 1, 1},
  {1, 1, 1, 1, 1, 1, 1, 1},
  {1, 1, 1, 1, 1, 1, 1, 1},
  {1, 1, 1, 1, 1, 1, 1, 1},
  {1, 1, 1, 1, 1, 1, 1, 1},
  {1, 1, 1, 1, 1, 1, 1, 1}
};
const int LETTER_O[8][8] = {
  {0, 1, 1, 1, 1, 1, 1, 0},
  {1, 1, 1, 0, 0, 1, 1, 1},
  {1, 1, 0, 0, 0, 0, 1, 1},
  {1, 1, 0, 0, 0, 0, 1, 1},
  {1, 1, 0, 0, 0, 0, 1, 1},
  {1, 1, 0, 0, 0, 0, 1, 1},
  {1, 1, 1, 0, 0, 1, 1, 1},
  {0, 1, 1, 1, 1, 1, 1, 0}
};
const int LETTER_K[8][8] = {
  {0, 1, 1, 0, 0, 0, 1, 1},
  {0, 1, 1, 0, 0, 1, 1, 0},
  {0, 1, 1, 0, 1, 1, 0, 0},
  {0, 1, 1, 1, 1, 0, 0, 0},
  {0, 1, 1, 0, 1, 1, 0, 0},
  {0, 1, 1, 0, 0, 1, 1, 0},
  {0, 1, 1, 0, 0, 0, 1, 1},
  {0, 1, 1, 0, 0, 0, 0, 1}
};
const int TEST[8][8] = {
  {0, 1, 1, 1, 1, 1, 1, 0},
  {1, 1, 1, 0, 0, 1, 1, 1},
  {1, 1, 0, 0, 0, 0, 1, 1},
  {1, 1, 0, 0, 0, 0, 1, 1},
  {1, 1, 0, 0, 0, 0, 1, 1},
  {1, 1, 0, 0, 0, 0, 1, 1},
  {1, 1, 1, 0, 0, 1, 1, 1},
  {0, 1, 1, 1, 1, 1, 1, 0}
};
const int ARROW_LEFT[8][8] = {
  {0, 0, 0, 1, 0, 0, 0, 0},
  {0, 0, 1, 1, 0, 0, 0, 0},
  {0, 1, 0, 1, 1, 1, 1, 1},
  {1, 0, 0, 0, 0, 0, 0, 1},
  {1, 0, 0, 0, 0, 0, 0, 1},
  {0, 1, 0, 1, 1, 1, 1, 1},
  {0, 0, 1, 1, 0, 0, 0, 0},
  {0, 0, 0, 1, 0, 0, 0, 0}
};
const int ARROW_RIGHT[8][8] = {
  {0, 0, 0, 0, 1, 0, 0, 0},
  {0, 0, 0, 0, 1, 1, 0, 0},
  {1, 1, 1, 1, 1, 0, 1, 0},
  {1, 0, 0, 0, 0, 0, 0, 1},
  {1, 0, 0, 0, 0, 0, 0, 1},
  {1, 1, 1, 1, 1, 0, 1, 0},
  {0, 0, 0, 0, 1, 1, 0, 0},
  {0, 0, 0, 0, 1, 0, 0, 0}
};
const int PLAYER_1[8][8] = {
  {0, 0, 0, 1, 1, 0, 0, 0},
  {0, 0, 1, 1, 1, 0, 0, 0},
  {0, 0, 0, 1, 1, 0, 0, 0},
  {0, 0, 0, 1, 1, 0, 0, 0},
  {0, 0, 0, 1, 1, 0, 0, 0},
  {0, 0, 0, 1, 1, 0, 0, 0},
  {0, 0, 0, 1, 1, 0, 0, 0},
  {0, 0, 1, 1, 1, 1, 0, 0}
};
const int PLAYER_2[8][8] = {
  {0, 0, 0, 1, 1, 0, 0, 0},
  {0, 0, 1, 0, 0, 1, 0, 0},
  {0, 0, 0, 0, 0, 1, 0, 0},
  {0, 0, 0, 0, 0, 1, 0, 0},
  {0, 0, 0, 0, 1, 0, 0, 0},
  {0, 0, 0, 1, 0, 0, 0, 0},
  {0, 0, 1, 0, 0, 0, 0, 0},
  {0, 0, 1, 1, 1, 1, 0, 0}
};

Credits

Mirko Pavleski

Mirko Pavleski

150 projects • 1283 followers

Comments