Musical Connect Four

I made a musical connect four.
To play it, you need a connected midi keyboard or an other device or software capable of sending note_on events. You need to connect your device in the settings section of the program.
Have fun.

#---settings---
#name of your device
device = "/midi:masterkey_61_0:1"


#---setup---
#the name of each note
note_name = (ring "c", "d", "e", "f", "g", "a", "b")

#the lowest free position of the column
lowest = (knit 5, 7)

#game board
board = (knit 0, 42)

#filters out the black keys
k_filter = (ring 0, -1, 1, -1, 2, 3, -1, 4, -1, 5, -1, 6)

#player's pieces
p_piece = (ring "☺", "☻")

#active player
a_player = 0

#how far each direction moves along y and x
d_shift_y = (ring 0, 1, 1, -1)
d_shift_x = (ring 1, 0, 1, 1)

#how many rows and colums are relevant to each direction
d_relevant_rows = (ring 6, 3, 3, 3)
d_relevant_columns = (ring 4, 7, 4, 4)

#starting row for each direction
d_starting_row = (ring 0, 0, 0, 3)

#are four in a row?
four_row = false

#is the game over?
victory = false

#is it a draw?
draw = false

#play place sound?
place = false

#play victory sound?
v_sound = false

#end drum sample
end_drum = :tabla_na_o
load_sample end_drum

#place drum sample
place_drum = :tabla_na_s
load_sample place_drum

#victory drum sample
v_drum = :drum_cymbal_hard
load_sample v_drum

#synths for players (offset by one)
p_synth = (ring :pluck, :piano)

#columns that belong to the victory row
v_column = (knit 6, 7)

#is the victory row vertical?
v_c_vertical = false

#extended scale
e_scale = (scale :e, :major_pentatonic)
e_scale = e_scale + (chord_invert e_scale, 6)



#---functions---
#overwriting specific indices
define :overwrite do |list, length, position, value|
  
  #empty return list
  outlist = (ring)
  
  #iterate through list
  tick_reset :over
  length.times do
    
    #Is this the position that is supposed to be overwritten?
    if  tick(:over) == (range 0, length)[position]
      
      #insert the desired value here
      outlist = outlist + (ring value)
      
    else
      
      #copy the value from the original list and insert it here
      outlist = outlist + (ring list.look(:over))
      
    end
    
  end
  
  #return the return list
  overwrite = outlist
  
end


#---game loop---
live_loop :game do
  
  #remove delay
  use_real_time
  
  if draw
    
    #say it's a draw
    puts "It's a draw!!!"
    puts "Try again!"
    puts
    
    #play victory sound
    v_sound = true
    
  elsif victory
    
    #say who won
    puts p_piece[a_player+1]+ " has won!!!"
    puts "Congratulations!"
    puts
    
    #play victory sound
    v_sound = true
    
  else
    
    #say who's turn it is
    puts p_piece[a_player]+ "'s turn:"
    puts
    
  end
  
  #iterate through rows
  tick_reset :row
  6.times do
    tick :row
    
    #output string contains only far left spacer
    output_string = "|"
    
    #iterate through columns
    tick_reset :column
    7.times do
      tick :column
      
      #what is on that field?
      if look(:row) == lowest.look(:column)
        
        #the field contains an option indicator for the column
        piece = note_name.look(:column)
        
      elsif board[look(:row)*7 + look(:column)] == 1
        
        #the field is occupied by player 1
        piece = p_piece[0]
        
      elsif board[look(:row)*7 + look(:column)] == 2
        
        #the field is occupied by player 2
        piece = p_piece[1]
        
      else
        
        #the field is empty
        piece = " "
        
      end
      
      #append piece and spacer
      output_string = output_string + piece + "|"
      
      
    end
    
    #print the finished row
    puts output_string
    
  end
  
  #get user input
  note, velocity = sync device + "/note_on"
  
  #is the game won?
  if victory
    
    #reset the lowest free position of the column
    lowest = (knit 5, 7)
    
    #reset game board
    board = (knit 0, 42)
    
    #reset active player
    a_player = 0
    
    #restart game
    victory = false
    
    #reset draw
    draw = false
    
    #reset columns that belong to the victory row
    v_column = (knit 6, 7)
    
  else
    
    #is this a black key?
    if k_filter[note] == -1
      
    elsif lowest[k_filter[note]] > -1
      
      #play place sound
      place = true
      
      #place piece on board
      board = overwrite(board, 42, lowest[k_filter[note]]*7 + k_filter[note], a_player+1)
      
      #update lowest free spot in column
      lowest = overwrite(lowest, 7, k_filter[note], lowest[k_filter[note]]-1)
      
      #test for win
      #iterate through directions
      tick_reset :dir
      4.times do
        tick :dir
        
        #iterate through relevant rows
        tick_set :r_row, d_starting_row.look(:dir)
        d_relevant_rows.look(:dir).times do
          tick(:r_row)
          
          #iterate through relevant columns
          tick_reset :r_column
          d_relevant_columns.look(:dir).times do
            tick(:r_column)
            
            #set current position as inspection point
            inspect_y = look(:r_row)
            inspect_x = look(:r_column)
            
            #is this space occupied by active player?
            if board[inspect_y*7 + inspect_x] == a_player+1
              
              #set four_row to true
              four_row = true
              
              #reset potential victory column
              potential_v_c = (knit 6, 7)
              
              #enter initial victory column
              potential_v_c = overwrite(potential_v_c, 7, inspect_x, range(0, 6)[inspect_y])
              
              #check the other pieces in the row
              3.times do
                
                #move to next inpection point
                inspect_y = range(0, 6)[inspect_y + d_shift_y.look(:dir)]
                inspect_x = range(0, 6)[inspect_x + d_shift_x.look(:dir)]
                
                #add to victory cloumn
                potential_v_c = overwrite(potential_v_c, 7, inspect_x, inspect_y)
                
                #is this space not occupied by active player?
                if board[inspect_y*7 + inspect_x] != a_player+1
                  
                  #set four_row to false
                  four_row = false
                  
                end
                
              end
              
              #were there 4 in a row?
              if four_row
                
                #we have a winner
                victory = true
                
                #determine victory positions
                #are the four positions in the same column?
                if look(:dir) == 1
                  
                  v_c_vertical = true
                  v_column = overwrite(v_column, 7, inspect_x, inspect_y)
                  
                else
                  
                  v_c_vertical = false
                  #confirm potential victory column
                  v_column = potential_v_c
                  
                end
                
              end
              
            end
            
          end
          
        end
        
      end
      
      #test for draw
      #set draw to true
      draw = true
      
      #iterate through board
      tick_reset :draw
      7.times do
        
        #is there a free space
        if lowest.tick(:draw) != -1
          #set draw to false
          draw = false
        end
        
      end
      
      #is there a draw?
      if draw
        
        #end the game
        victory = true
        
      end
      
      #change player
      a_player = (a_player-1)*-1
      
    end
    
  end
  
end


#---audio loop---
live_loop :audio do
  
  #remove debug output
  use_debug false
  
  #speed things up
  use_bpm 200
  
  #advance to next step
  tick
  
  if (range 0, 8).look == 7
    sample end_drum
    
  elsif v_column[(range 0, 8).look] != 6
    v_note = 5 - v_column[(range 0, 8).look]
    synth p_synth[a_player], note: e_scale[v_note]
    synth p_synth[a_player], note: e_scale[v_note + 2]
    if v_c_vertical
      synth p_synth[a_player], note: e_scale[v_note + 4]
      synth p_synth[a_player], note: e_scale[v_note + 6]
    end
    
    
  elsif lowest[(range 0, 8).look] != 5
    synth p_synth[board[(lowest[(range 0, 8).look]+1)*7+(range 0, 8).look]], note: e_scale[4 - lowest[(range 0, 8).look]]
    
  end
  
  if place
    place = false
    sample place_drum
  end
  
  if v_sound
    v_sound = false
    sample v_drum
  end
  
  #sleep
  sleep 1
  
end

There’s still some bugs, but I don’t think I have the motivation to fix them, at least for now.
Sometimes the victory sounds aren’t played like they are supposed to.