Created
June 1, 2015 20:37
-
-
Save vexorian/06c237f22d5a4befafa5 to your computer and use it in GitHub Desktop.
A ruby class that parses a @massconnect4 tweet and tries to return a reply as a string
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# encoding: UTF-8 | |
# A ruby class that parses a @massconnect4 tweet and tries to return a reply as | |
# a string. Your bot should handle the tweet detecting and reading. | |
# | |
# connect4 = MassConnect4Player.new( team, unclever) | |
# team is your team either :sun or :moon | |
# unclever is a float from 0.0 to 1.0 , the higher this is the worse it will play | |
# | |
# connect4.play(tweet_text) , take a tweet text from @massconnect4, return a reply | |
# or nil if something went wrong. | |
# | |
# The current game logic is very bareboned, at least it doesn't fall to straight | |
# obvious traps (even with 1.0 uncleverness), however. I guess you could build | |
# a real AI from this. I guess. | |
# | |
# zlib licence, vexorian 2015 | |
# | |
class MassConnect4Player | |
def initialize(team, unclever) | |
@unclever = unclever | |
if team == :sun | |
@team = :sun | |
@enemy = :moon | |
else | |
@team = :moon | |
@enemy = :sun | |
end | |
end | |
def get_cell(board, x,y) | |
return board[ 7*(5-y) + x ] | |
end | |
def set_cell(board, x,y, c) | |
board[ 7*(5-y) + x ] = c | |
end | |
def count_bad_traps(board) | |
traps = 0 | |
for x in 0..6 | |
for y in 0..5 | |
for mov_y in [ [y,y+1,y+2,y+3], [y,y-1,y-2,y-3], [y,y,y,y] ] | |
for mov_x in [ [x,x+1,x+2,x+3], [x,x-1,x-2,x-3], [x,x,x,x] ] | |
x3 = mov_x[3] | |
y3 = mov_y[3] | |
if ( (x3 != x) || (y3 != y) ) && (0 <= x3 && x3 <= 6) && (0 <= y3 && y3 <= 5) | |
count_sun = 0 | |
count_moon = 0 | |
for i in 0..3 | |
c = get_cell(board, mov_x[i], mov_y[i]) | |
if c == @team | |
count_moon = count_moon + 1 | |
end | |
if c == @enemy | |
count_sun = count_sun + 1 | |
end | |
end | |
if (count_sun == 2) && (count_moon == 0) | |
traps = traps + 1 | |
end | |
end | |
end | |
end | |
end | |
end | |
return traps | |
end | |
def count_traps(board) | |
traps = 0 | |
for x in 0..6 | |
for y in 0..5 | |
for mov_y in [ [y,y+1,y+2,y+3], [y,y-1,y-2,y-3], [y,y,y,y] ] | |
for mov_x in [ [x,x+1,x+2,x+3], [x,x-1,x-2,x-3], [x,x,x,x] ] | |
x3 = mov_x[3] | |
y3 = mov_y[3] | |
if ( (x3 != x) || (y3 != y) ) && (0 <= x3 && x3 <= 6) && (0 <= y3 && y3 <= 5) | |
count_moon = 0 | |
good_empty = false | |
for i in 0..3 | |
c = get_cell(board, mov_x[i], mov_y[i]) | |
if c == @team | |
count_moon = count_moon + 1 | |
end | |
if c == :empty | |
if (mov_y[i] > 0) && (get_cell(board, mov_x[i], mov_y[i]-1) == :empty) | |
good_empty = true | |
end | |
end | |
end | |
if (count_moon == 3) && good_empty | |
traps = traps + 1 | |
end | |
end | |
end | |
end | |
end | |
end | |
return traps | |
end | |
def who_wins(board) | |
for x in 0..6 | |
for y in 0..5 | |
for mov_y in [ [y,y+1,y+2,y+3], [y,y-1,y-2,y-3], [y,y,y,y] ] | |
for mov_x in [ [x,x+1,x+2,x+3], [x,x-1,x-2,x-3], [x,x,x,x] ] | |
x3 = mov_x[3] | |
y3 = mov_y[3] | |
if ( (x3 != x) || (y3 != y) ) && (0 <= x3 && x3 <= 6) && (0 <= y3 && y3 <= 5) | |
# valid line | |
all_sun = true | |
all_moon = true | |
for i in 0..3 | |
c = get_cell(board, mov_x[i], mov_y[i]) | |
if c != :sun | |
all_sun = false | |
end | |
if c != :moon | |
all_moon = false | |
end | |
end | |
if all_sun | |
return :sun | |
end | |
if all_moon | |
return :moon | |
end | |
end | |
end | |
end | |
end | |
end | |
return :none | |
end | |
def play(tweet_text) | |
empty = "⬜" | |
sun = "🌞" | |
moon = "🌚" | |
board = [] | |
tweet_text.split("").each do |s| | |
t = :none | |
if s.eql?(empty) | |
t = :empty | |
elsif s.eql?(sun) | |
t = :sun | |
elsif s.eql?(moon) | |
t = :moon | |
end | |
if t != :none | |
board = board + [t] | |
end | |
end | |
if board.size != 42 | |
puts "massconnect wrong board size" | |
return nil | |
end | |
total_empty = board.count(:empty) | |
if total_empty == 42 | |
return "4" | |
end | |
if total_empty == 41 | |
if get_cell(board, 3,0) == :empty | |
return "4" | |
else | |
return ["3","5"].sample | |
end | |
end | |
win = [] | |
available = [] | |
#for y in [5,4,3,2,1,0] | |
# for x in 0..6 | |
# if get_cell(board,x,y) == :empty | |
# print "." | |
# elsif get_cell(board,x,y) == :sun | |
# print "*" | |
# elsif get_cell(board,x,y) == :moon | |
# print "o" | |
# else | |
# print "?" | |
# end | |
# end | |
# puts "" | |
#end | |
#puts "" | |
best = nil | |
for x in 0..6 | |
y = 0 | |
while (get_cell(board, x,y) != :empty) && (y <= 5) | |
y = y + 1 | |
end | |
if y <= 5 | |
# can make the move by placing moon in x,y | |
set_cell(board, x,y, @team) | |
if who_wins(board) == @team | |
win = win + [x] | |
else | |
valid = true | |
for x2 in 0..6 | |
y2 = 0 | |
while (get_cell(board, x2,y2) != :empty) && (y2 <= 5) | |
y2 = y2 + 1 | |
end | |
if y2 <= 5 | |
set_cell(board, x2,y2, @enemy) | |
if who_wins(board) == @enemy | |
valid = false | |
end | |
set_cell(board, x2,y2, :empty) | |
end | |
end | |
if valid | |
available = available + [x] | |
traps = count_traps(board) | |
bad_traps = count_bad_traps(board) | |
if (best == nil) || (best[:traps] < traps) || ( (best[:traps] == traps) && (best[:bad_traps] > bad_traps) ) | |
if best == nil | |
best = Hash.new | |
end | |
best[:move] = x | |
best[:traps] = traps | |
best[:bad_traps] = bad_traps | |
best[:count] = 1 | |
elsif (best[:traps] == traps) && (best[:bad_traps] == bad_traps) | |
best[:count] = best[:count] + 1 | |
if rand( 1 .. best[:count] ) == 1 | |
best[:move] = x | |
end | |
end | |
end | |
end | |
set_cell(board, x,y, :empty) | |
end | |
end | |
puts "best = " + best.to_s | |
puts "win = " + win.to_s | |
puts "available = " + available.to_s | |
if win.size > 0 | |
return (win.sample + 1).to_s | |
end | |
if available.size > 0 | |
if (best != nil) && (rand > @unclever) | |
return (best[:move] + 1).to_s | |
end | |
return (available.sample + 1).to_s | |
end | |
return nil # we lose | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment