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