Created
August 15, 2012 13:07
-
-
Save schrockwell/3359964 to your computer and use it in GitHub Desktop.
Yahtzee CLI v0.0.2
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
#!/usr/bin/env ruby | |
# encoding: utf-8 | |
# Yahtzee CLI | |
# Version 0.0.2 | |
# | |
# By Rockwell Schrock | |
# [email protected] | |
# https://gist.github.com/gists/3359964 | |
# | |
# Run it and have fun! | |
@dice = [6] * 5; | |
@scores = [{}] | |
@player_count = 1 | |
@current_player = 0 | |
TopCategories = [:ones, :twos, :threes, :fours, :fives, :sixes] | |
BottomCategories = [:three_of_a_kind, :four_of_a_kind, :full_house, :small_straight, :large_straight, :yahtzee, :chance] | |
Categories = TopCategories + [:bonus, :top_total] + BottomCategories + [:bottom_total] | |
Titles = ['Ones', 'Twos', 'Threes', 'Fours', 'Fives', 'Sixes', 'Bonus', 'Top Total', 'Three of a Kind', 'Four of a Kind', 'Full House', 'Small Straight', 'Large Straight', 'Yahtzee', 'Chance', 'Bottom Total'] | |
Letters = ['a', 'b', 'c', 'd', 'e', 'f', nil, nil, 'g', 'h', 'i', 'j', 'k', 'l', 'm', nil] | |
TitleColumnWidth = 24 | |
ScoreColumnWidth = 12 | |
class Array | |
def histogram | |
self.inject(Hash.new(0)) { |total, e| total[e] += 1; total } | |
end | |
def sum | |
self.inject(0) { |sum, i| sum += i; sum } | |
end | |
def max_index | |
self.each_with_index.max[1] | |
end | |
end | |
def draw_board(dice, scores, current_player, roll, show_total) | |
puts " ---- PLAYER #{current_player + 1} -----" if current_player | |
puts " ---- ROLL #{roll} -----" if roll | |
puts | |
puts ' ' * (3 + TitleColumnWidth) + (1..@player_count).to_a.inject('') { |s, p| s += "P#{p}" + ' ' * (ScoreColumnWidth - 2); s } | |
Categories.each_with_index do |c, i| | |
# Print letter | |
if Letters[i] | |
line = "#{Letters[i]}. " | |
else | |
line = ' ' | |
end | |
# Print title | |
line += Titles[i] | |
line += ' ' * (TitleColumnWidth - Titles[i].length) | |
scores.each_with_index do |player_scores, player_i| | |
# Print score | |
if player_scores[c] | |
line += player_scores[c].to_s | |
elsif can_score?(c, player_scores) && player_i == @current_player | |
line += "___ (#{score(dice, c, player_scores)})" | |
else | |
line += "___" | |
end | |
line += ' ' * ((TitleColumnWidth + 3 + (player_i + 1) * 12) - line.length) | |
end | |
puts line | |
puts if c == :top_total || c == :bottom_total | |
end | |
if show_total | |
line = ' Final Score' + ' ' * (TitleColumnWidth - 11) | |
total_scores(@scores).each do |total| | |
line << total.to_s + ' ' * (ScoreColumnWidth - total.to_s.length) | |
end | |
puts line | |
end | |
end | |
def total_scores(scores) | |
scores.map { |s| (s[:top_total] || 0) + (s[:bottom_total] || 0) } | |
end | |
def can_score?(category, scores) | |
return false if [:bonus, :top_total, :bottom_total].index(category) | |
if category == :yahtzee | |
return scores[:yahtzee] == nil || scores[:yahtzee] == 50 | |
else | |
return scores[category] == nil | |
end | |
end | |
def score(dice, category, scores) | |
case category | |
when :ones, :twos, :threes, :fours, :fives, :sixes | |
i = Categories.index(category) + 1 | |
return dice.count(i) * i | |
when :bonus | |
if top_total(scores) >= 63 | |
return 35 | |
else | |
return 0 | |
end | |
when :three_of_a_kind | |
(1..6).each do |i| | |
return dice.sum if dice.count(i) >= 3 | |
end | |
return 0 | |
when :four_of_a_kind | |
(1..6).each do |i| | |
return dice.sum if dice.count(i) >= 4 | |
end | |
return 0 | |
when :full_house | |
return 25 if dice.histogram.values.sort == [2, 3] | |
return 0 | |
when :small_straight | |
(1..3).each do |start| | |
return 30 if (dice & (start..(start + 3)).to_a).length == 4 | |
end | |
return 0 | |
when :large_straight | |
(1..2).each do |start| | |
return 40 if (dice & (start..(start + 4)).to_a).length == 5 | |
end | |
return 0 | |
when :yahtzee | |
(1..6).each do |i| | |
if dice.count(i) == 5 | |
return 50 if scores[:yahtzee] == nil | |
return 150 if scores[:yahtzee] == 50 | |
end | |
end | |
return 0 | |
when :top_total | |
return top_total(scores) | |
when :bottom_total | |
return bottom_total(scores) | |
when :chance | |
return dice.sum | |
end | |
end | |
def done?(scores) | |
(scores.keys & Categories).length == Categories.length | |
end | |
def update_scores(scores) | |
if (scores.keys & TopCategories).length == TopCategories.length | |
scores[:bonus] = score(nil, :bonus, scores) | |
scores[:top_total] = score(nil, :top_total, scores) | |
end | |
if (scores.keys & BottomCategories).length == BottomCategories.length | |
scores[:bottom_total] = score(nil, :bottom_total, scores) | |
end | |
scores | |
end | |
def draw_dice(dice) | |
lines = [''] * 5 | |
dice.each do |i| | |
lines[0] += ' _____ ' | |
lines[4] += ' ----- ' | |
case i | |
when 1 | |
lines[1] += '| | ' | |
lines[2] += '| • | ' | |
lines[3] += '| | ' | |
when 2 | |
lines[1] += '| • | ' | |
lines[2] += '| | ' | |
lines[3] += '| • | ' | |
when 3 | |
lines[1] += '| • | ' | |
lines[2] += '| • | ' | |
lines[3] += '| • | ' | |
when 4 | |
lines[1] += '| • • | ' | |
lines[2] += '| | ' | |
lines[3] += '| • • | ' | |
when 5 | |
lines[1] += '| • • | ' | |
lines[2] += '| • | ' | |
lines[3] += '| • • | ' | |
when 6 | |
lines[1] += '| • • | ' | |
lines[2] += '| • • | ' | |
lines[3] += '| • • | ' | |
end | |
end | |
lines.each { |line| puts line } | |
end | |
def top_total(scores) | |
(TopCategories + [:bonus]).inject(0) { |sum, c| sum += scores[c] || 0 ; sum } | |
end | |
def bottom_total(scores) | |
BottomCategories.inject(0) { |sum, c| sum += scores[c] || 0 ; sum } | |
end | |
def roll_dice(dice, keepers) | |
new_dice = []; | |
keepers.each do |k| | |
i = dice.index(k) | |
if i | |
new_dice << k | |
dice.delete_at(i) | |
end | |
end | |
(new_dice.count..4).each do | |
new_dice << rand(6) + 1 | |
end | |
new_dice | |
end | |
MaxRolls = 3 | |
MaxDice = 5 | |
def valid_input?(input, scores, dice, roll) | |
if input.length == 1 and input.match(/[a-mA-M]/) | |
# Scoring input | |
category_index = Letters.index(input.downcase) | |
category = Categories[category_index] | |
if can_score?(category, scores) | |
return true | |
else | |
puts "Already scored a #{Titles[category_index]}" | |
return false | |
end | |
elsif input.length <= MaxDice and roll < MaxRolls | |
# Keeper input | |
matches = input.match(/[1-6]*/) | |
if matches == nil or matches[0] != input | |
puts 'Invalid input' | |
return false | |
end | |
return true | |
else | |
puts 'You must select a scoring category' if roll == MaxRolls | |
puts 'Invalid input: too many dice' if input.length > MaxDice | |
return false | |
end | |
end | |
def play | |
puts 'Number of players?' | |
@player_count = gets.to_i | |
@scores = [] | |
(1..@player_count).each do | |
@scores << {} | |
end | |
while true do | |
keepers = [] | |
(1..3).each do |roll| | |
@dice = roll_dice(@dice, keepers) | |
draw_board(@dice, @scores, @current_player, roll, false) | |
draw_dice(@dice) | |
while true do | |
if roll == 3 | |
puts 'Enter a letter to score' | |
input_string = gets.chomp | |
else | |
puts 'Enter keeper dice or a letter to score' | |
input_string = gets.chomp | |
end | |
break if valid_input?(input_string, @scores[@current_player], @dice, roll) | |
end # input loop | |
if Letters.index(input_string.downcase) | |
c = Categories[Letters.index(input_string.downcase)] | |
@scores[@current_player][c] = score(@dice, c, @scores[@current_player]) | |
@scores[@current_player] = update_scores(@scores[@current_player]) | |
break | |
else | |
keepers = [] | |
input_string.each_char do |c| | |
keepers << c.to_i | |
end | |
end | |
end # roll loop | |
game_over = true | |
(1..@player_count).each do | |
@current_player = (@current_player + 1) % @player_count | |
if not done?(@scores[@current_player]) | |
game_over = false | |
break | |
end | |
end | |
break if game_over | |
end # turn loop | |
draw_board(@dice, @scores, nil, nil, true) | |
totals = total_scores(@scores) | |
winner = totals.max_index | |
puts | |
puts "PLAYER #{winner + 1} " + ['DOMINATED!!!', 'KICKED ASS!!!', 'PWNED!!!', 'NAILED IT!!!', 'GOT A HEADSHOT!!!', 'IS KING!!!', 'GETS AN UPVOTE!!!'].sample | |
end | |
play |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment