Help me refactor my World Cup Challenge Script - ruby

I am setting up a World Cup Challenge between some friends, and decided to practice my Ruby and write a small script to automate the process.
The Problem:
32 World Cup qualifiers split into 4 tiers by their Fifa ranking
8 entries
Each entry is assigned 1 random team per tier
Winner takes all :-)
I wrote something that suffices yet is admittedly brute force. But, in my attempt to improve my Ruby, I acknowlege that this code isn't the most elegant solution around - So I turn to you, the experts, to show me the way.
It may be more clear to check out this gist - https://gist.github.com/91e1f1c392bed8074531
My Current (poor) solution:
require 'yaml'
#teams = YAML::load(File.open('teams.yaml'))
#players = %w[Player1 Player2 Player3 Player4 Player5 Player6 Player7 Player8]
results = Hash.new
players = #players.sort_by{rand}
players.each_with_index do |p, i|
results[p] = Array[#teams['teir_one'][i]]
end
second = #players.sort_by{rand}
second.each_with_index do |p, i|
results[p] << #teams['teir_two'][i]
end
third = #players.sort_by{rand}
third.each_with_index do |p, i|
results[p] << #teams['teir_three'][i]
end
fourth = #players.sort_by{rand}
fourth.each_with_index do |p, i|
results[p] << #teams['teir_four'][i]
end
p results
I am sure there is a better way to iterate through the tiers, and duplicating the #players object ( dup() or clone() maybe?)
So from one Cup Fan to another, help me out.

I'm still learning Ruby too, but here's an attempt:
require 'yaml'
tiers = YAML::load(File.open('world_cup_tiers.yaml'))
players = %w[P1 P2 P3 P4 P5 P6 P7 P8]
draws = Hash.new { |h,k| h[k] = [] }
tiers.each do |tier, teams|
players.zip(teams.sort_by{rand}).each do |tuple|
player, team = tuple
draws[player].push(team)
end
end
players.each { |player| puts [player, draws[player]].join(' ') }

Related

How to implement Aces in 5-Card-Draw game?

I'm taking an online course in Ruby programming and I need to make 5-Card Draw game as one of the projects. It all went well until I realized that Ace can have two values.
I've made 3 classes so far: Card, Deck and Hand. I'm currently working on a Hand class. The other two classes are below:
class Card
attr_reader :number, :sign, :color
def initialize(number, sign, color)
#number = number
#sign = sign
#color = color
end
end
require_relative 'card.rb'
class Deck
def initialize
#deck = make_deck
end
def make_deck
deck = []
signs = {'Club' => 'black', 'Spade' => 'black', 'Heart' => 'red', 'Diamond' => 'red'}
n = 1
while n < 15
if n == 11
n += 1
next
end
i = 0
4.times do
sign = signs.keys[i]
color = signs[sign]
deck << Card.new(n, sign, color)
i += 1
end
n += 1
end
deck
end
end
So, the problem appeared when I started coding the Poker Hands in Hand class. I'm not sure how to deal with the Ace because it can have a value of either 1 or 15. Any help/suggestion is welcomed.
"Ace can have two values" isn't the right way to think of it. Just make Aces high, always. Then, in the code that checks for straights you have to special-case the wheel. That is, a straight is defined as "5 cards in rank sequence, or A-2-3-4-5".

ruby: Rock Scissors Paper program. How to make algorithm function properly?

I'm creating a rock, scissors, paper program for my first program with ruby.
I'm thinking of using a hash called #wins to determine if a certain combination wins. If both hands are the same, the result is a Draw!. Otherwise, the result is Lose!.
I'm struggling to implement the algorithm to judge the results. How do I check if a given combination exists within the #wins hash to judge wether it's a win or now?
class Hand
attr_accessor :shape
##shapes = [:rock, :scissors, :paper]
def generate
#shape = ##shapes[rand(3)]
end
end
class Game
#wins = {rock: :scissors, scissors: :paper, paper: :rock}
def judge(p1, p2)
'Win!' if (a way to see if a given combination exists within a #wins hash) # Not working
'Draw!' if p1 == p2 # Not working
else 'Lose!'
end
end
player_hand = Hand.new
player_hand.shape = ARGV.join.to_sym
puts player_hand.shape # Debug
computer_hand = Hand.new
computer_hand.shape = computer_hand.generate
puts computer_hand.shape # Debug
game = Game.new
puts game.judge(player_hand.shape, computer_hand.shape)
def judge(p1, p2)
case
when #wins[p1] == p2 then "Win"
when #wins[p2] == p1 then "Lose"
else "Draw"
end
end

Rock paper scissor - homework

In a game of rock-paper-scissors, each player chooses to play Rock (R), Paper (P), or Scissors (S). The rules are: Rock breaks Scissors, Scissors cuts Paper, but Paper covers Rock. In a round of rock-paper-scissors, each player's name and strategy is encoded as an array of two elements. Create a RockPaperScissors class with a class method winner that takes two 2-element arrays like those above, and returns the one representing the winner:
RockPaperScissors.winner(['Armando','P'], ['Dave','S']) # => ['Dave','S']
If either player's strategy is something other than "R", "P" or "S" (case-SENSITIVE), the method should raise a 'RockPaperScissors::NoSuchStrategyError' exception and provide the message: "Strategy must be one of R,P,S"
If both players use the same strategy, the first player is the winner.
I have my code below. My code is not comparing the two strings correctly in the line
(#p1[1,1]==rules["#{p}"]?#p1:#p2).
Please help me out.
class RockPaperScissors
def winner(p1,p2)
#p1 = p1
#p2 = p2
p = (#p1[1,1]+#p2[1,1]).sort
rules = Hash.new(0)
rules = {"PR"=>"R","PS"=>"S", "RS"=>"R", "PP"=>"1","RR"=>"1","SS"=>"1"}
if rules["#{p}"].nil?
raise RockPaperScissors::NoSuchStrategyError,"Strategy must be one of R,P,S"
elseif rules["#{p}"]=="1"
return #p1
else
print #p1[1,1]
print rules["#{p}"]
#p1[1,1]==rules["#{p}"]?#p1:#p2
end
end
end
t = RockPaperScissors.new
print t.winner(['Armando','R'], ['Dave','S'])
Some general tips: You don't need [1,1], [1] or .last would be better. Also no need to initialize rules to a new hash, you can only keep the line rules = {"PR"=>"R".... puts is more commonly used than print. You're overthinking this a bit. Maybe clean up your code, try to simplify it with the tips posted above and see if this gets you unstuck.
Once you are done, have a look at what an idiomatic Ruby solution could look like, but don't submit it as your solution:
module RockPaperScissors
VALID_STRATEGIES = %i(R P S)
RULES = { R: :S, P: :R, S: :P }
def self.winner(p1, p2)
choice1, choice2 = p1.last.intern, p2.last.intern
unless [choice1, choice2].all? { |s| VALID_STRATEGIES.include? s }
raise RockPaperScissors::NoSuchStrategyError, "Strategy must be one of R,P,S"
end
return p1 if choice1 == choice2
RULES[choice1] == choice2 ? p1 : p2
end
end
When you use the [1,1] on an array, you receive an array of size 1 starting from index 1:
[1,2,3][1,1]
# => [2]
[1,2,3][1]
# => 2
Because of that when you compare it to the rules, you never get true, since no rule is an array...
["S"] == "S"
# => false
So to fix your code, instead of
p = (#p1[1,1]+#p2[1,1]).sort
# ...
(#p1[1,1]==rules["#{p}"]?#p1:#p2)
You should try:
p = (#p1[1]+#p2[1]).sort
# ...
(#p1[1]==rules[p]?#p1:#p2

Determining a poker hand with two arrays

I want to create a simple multiplayer poker program in ruby, but I feel like I'm reinventing the wheel:
table = [card1, card2, card3, card4, card5]
mike = [card6, card7]
john = [card8, card9]
card6.face = "A"
card6.suit = "spades"
I'm having a hard time writing an algorithm to decide which of the possible hands each player has.
For example, in order to determine if a player was dealt a flush I wrote something like this:
together = table + hand
# Populate hash with number of times each suit was found
$suits.each do |suit|
matched[suit] = together.select{|card| card.suit == suit}.size
end
matched.each do |k,v|
if v == 5
puts "#{k} were seen five times, looks like a flush."
end
end
This doesn't seem very comprehensive (No way to tell if it's an Ace-high or a 6-high flush) nor very ruby-like.
Is there a more obvious way to determine hands in poker?
It's probably far from perfect, but I wrote some methods to detect poker hands to solve a project euler problem. Maybe it can give you some ideas ; full code is here: https://github.com/aherve/Euler/blob/master/pb54.rb
In a nutshell, Hand is defined by an array of Card, that respond to Card.value and Card.type:
def royal_flush?
return #cards if straight_flush? and #cards.map(&:value).max == Poker::values.max
end
def straight_flush?
return #cards if straight? and #cards.map(&:type).uniq.size == 1
end
def four_of_a_kind?
x_of_a_kind?(4)
end
def full_house?
return #hand if three_of_a_kind? and Hand.new(#cards - three_of_a_kind?).one_pair?
return nil
end
def flush?
return #cards if #cards.map(&:type).uniq.size == 1
end
def straight?
return #cards if (vs = #cards.map(&:value).sort) == (vs.min..vs.max).to_a
end
def three_of_a_kind?
x_of_a_kind?(3)
end
def two_pairs?
if (first_pair = one_pair?) and (second = Hand.new(#cards - one_pair?).one_pair?)
return first_pair + second
else
return false
end
end
def one_pair?
x_of_a_kind?(2)
end
def high_card?
#cards.sort_by{|c| c.value}.last
end
private
def x_of_a_kind?(x)
Poker::values.each do |v|
if (ary = #cards.select{|c| c.value == v}).size == x
return ary
end
end
return false
end
end

Ruby RPG: undefined method for creature traits

Ok, so I've written a few simple RPGs before in other languages (namely python), so I decided to try writing one in ruby. So, this morning, I sat down and typed up the basics of combat mechanics, made a few creatures, and started on the story. However, for some reason, when I try to run the program I get this error:
herosquest.rb:67: undefined method 'traits' for Creature::Hero:Class (NoMethodError)
I looked back at the code (which I'll provide in full at the end) and found the definition in lines 26 to 55, exactly where it should be.
class Creature
# Creature attributes are used to determine the outcome of combat.
traits :life, :strength, :charisma, :weapon
attr_accessor :life, :strength, :charisma, :weapon
#New class methods for hp, str, char, wep
def self.life( val )
#traits ||= {}
#traits['life'] = val
end
def self.strength( val )
#traits ||= {}
#traits['strength'] = val
end
def self.charisma( val )
#traits ||= {}
#traits['charisma'] = val
end
def self.weapon( val )
#traits ||= {}
#traits['weapon'] = val
end
#initialize sets defaults for each attr
def initialize
self.class.traits.each do |k,v|
instance_variable_set("##{k}", v)
end
end
end
Why isn't it working?
Here's the rest of the code, if that helps.
Note: When I copied my code, some of the indents got screwed up. It's much neater than it looks.
#CREATURE MECHANICS
#Create critters. Make them do stuff.
class Creature
def self.metaclass; class << self; self; end; end
def self.traits( *arr )
return #traits if arr.empty?
attr_accessor( *arr )
arr.each do |a|
metaclass.instance_eval do
define_method( a ) do |val|
#traits ||= {}
#traits[a] = val
end
end
end
class_eval do
define_method( :initialize ) do
self.class.traits.each do |k,v|
instance_variable_set("##{k}", v)
end
end
end
end
class Creature
# Creature attributes are used to determine the outcome of combat.
traits :life, :strength, :charisma, :weapon
attr_accessor :life, :strength, :charisma, :weapon
#New class methods for hp, str, char, wep
def self.life( val )
#traits ||= {}
#traits['life'] = val
end
def self.strength( val )
#traits ||= {}
#traits['strength'] = val
end
def self.charisma( val )
#traits ||= {}
#traits['charisma'] = val
end
def self.weapon( val )
#traits ||= {}
#traits['weapon'] = val
end
#initialize sets defaults for each attr
def initialize
self.class.traits.each do |k,v|
instance_variable_set("##{k}", v)
end
end
end
#CREATURE TYPES
#Hero
#Our brave Hero! Chosen by the gods! Mighty slayer
#of beasts! Also, broke. Which is why he's hunting
#a dragon. Poor, foolish mortal...
class Hero < Creature
traits :bombs, :arrows, :money
life 10
strength 4
charisma 4
weapon 4
bombs rand(3..10)
arrows rand(15..100)
money rand(0..25) #Should be used with Traders for buying stuff
#Bow. Needs arrows. Duh. Hero should start with, say, 50?
def ^( enemy )
if #arrows.zero?
puts "[Twang. Your imaginary arrow, alas, fails to slay the creature. Or have any effect at all. Moron.]"
return
end
#arrows -= 1
fight( enemy, rand(0+((charisma+(0..4))*2))) #Zero means you missed
end
end
#The hero's sword, given to him by the gods.
#Unlimited uses. Also, shiny.
def /( enemy )
fight( enemy, rand(4+weapon+(strength*2)))
end
#Trading. Ah, commerce! Doesn't quite work yet.
#Need a way to make sure money stays positive,
#and some sort of way to make sure you can't
#cheat the poor trader out of his goods.
def trade( enemy )
if #money.zero?
puts "[ The trader ignores you. You need money.]"
return
else puts "[What do you want to buy?\n Arrows = 10 for 2 coins\n Bombs = 5 for 5 coins]" #Should add new weapons someday
purchase = gets.chomp
if purchase == "Arrows"
puts "[You buy 10 arrows for 2 coins]"
#arrows += 10
#money -= 2
elsif purchase == "Bombs"
puts "[You buy 5 bombs for 5 coins]"
#bombs += 5
#money -= 5
end
end
#Mmm, tasty lembas. Elvish waybread. One small
#bite is enough to fill the stomach of a grown man.
def %( enemy )
lembas = rand( charisma )
puts "[Tasty lembas gives you #{ lembas } life.]"
#life += lembas
fight( enemy, 0 )
end
#Bombs. They explode. Hopefully, far away from you.
def *( enemy )
if #bombs.zero?
puts "[You light your finger on fire instead of the fuze. Then, you realize you're out of bombs.]"
#life -= 1
return
end
#bombs -= 1
fight( enemy, rand(weapon+(2..25)))
end
end
#Man
#Doesn't do much. Stands around and says boring things. Well, someday, anyway...
class Man < Creature
traits :chat #Will eventually say random stuff about the weather or whatever.
life 4
strength 2
charisma 4
weapon 1
end
#Woman
#Like Man. Except she smells nicer.
#Oh, and she's got a frying pan...
#Looks fierce.
class Woman < Creature
life 4
strength 2
charisma 8
weapon 4
end
#Trader
#Hangs around taverns and pushes
#useless stuff on passers-by.
class Trader < Creature
traits :bombs, :arrows, :money
life 4
strength 4
charisma 16
weapon 10 #Armed and dangerous. Sort of...
bombs rand(0..100)
arrows rand(0..500)
money rand(100..2500)
end
#Monkey
#Pain in the butt. Travels in packs. Makes
#faces and throws bananas.
class Monkey < Creature
life 8
strength 5
charisma 11
weapon 2 #Beats you with its cute little fists.
end
#Cow
#Cows won't attack you unless you hit them, or try to ride them,
#or make fun of them for chewing cud.
#Also, they don't like it when people stare
#at their udders. It makes them uncomfortable.
class Cow < Creature
life 18
strength 15
charisma 2 #It's a cow, not a rocket scientist
weapon 3
end
#Deer
#The deer is not as timid as it appears. Serves you right
#for picking on a poor, "helpless" creature. Jerk.
class Deer < Creature
life 30
strength 22
charisma 5
weapon 15
end
#Biker
#Found on paths. Rides a bike.
#Not *super* tough, but should be
#strong enough to make you fear
#for your life.
class Biker < Creature
life 34
strength 10
charisma 4 #They aren't very bright...
weapon 8
end
#Angel
#Heavenly host. "Information: Kill"
#Seriously, don't screw around with these guys.
class Angel < Creature
life 85
strength 8
charisma 60 #Seriously smart, since it's
weapon 25 #secretly a robot.
end
#Tentacle
#Found in lakes by throwing things ala Merry and Pippin.
#NOTE: Should include this in the first scene with a lake:
#"I am afraid of the pool. Don't disturb it!"
#Should preferably be said by a smallish man, muttering about
#"My precious," Mordor, and someone called Sam.
class Tentacle < Creature
life 9
strength 3 #Not strong, but there's a lot of them.
charisma 4
weapon 8
end
#Watcher in the Water
#Found after killing a bunch of tentacles.
#Should be tough enough to scare you off,
#but weak enough that you won't die
#instantly.
class Watcher_in_the_Water < Creature
life 250
strength 10
charisma 35
weapon 4 #I don't want him to be too powerfull
end
#Dragon
#Will totally kill you to death. A lot.
#Final boss type thing. Surrounded
#by his hoard of glittering treasure.
#Oooh, shiny...
class Dragon < Creature
life 500 # tough scales
strength 95 # bristling veins
charisma 65 # toothy smile
weapon 157 # fire breath
end
#COMBAT MECHANICS
#Hitting in combat
def hit( damage )
p_up = rand( charisma )
if p_up % 9 == 7
#life += p_up / 4
puts "[#{ self.class } magic powers up #{ p_up }!]"
end
#life -= damage
puts "[#{ self.class } has died.]" if #life <= 0
end
#Turn in combat
def fight( enemy, weapon )
if life <= 0
puts "[#{ self.class } is dead, Jim. You can't fight!]"
return
end
#You attack
your_hit = rand( strength + weapon )
puts "[Your #{weapon} hit the #{enemy} for #{ your_hit } points of damage!]"
enemy.hit( your_hit )
#Enemy fights back
p enemy
if enemy.life > 0
enemy_hit = rand( enemy.strength + enemy.weapon )
puts "[The #{enemy} hit with #{ enemy_hit } points of damage!]"
self.hit( enemy_hit )
end
end
end
#PLACES, EVENTS, & STORY
#First, generate the Player
h = Hero.new
#Determine player name and gender
print "What is your name, adventurer?\n"
name=gets.chomp.to_s
print "#{name}, are you male or female?\n"
gender=gets
print "\n"
#player chooses his/her class
until heroclass = (1..3)
print"What class do you want?\n(1 for Scout, 2 for Fighter, or 3 for Ranger)\n"
heroclass = gets.to_i
if heroclass == 1
heroclass = "Scout"
Hero.charisma = charisma + 3
Hero.strength = strength - 1
Hero.weapon = weapon + 1
Hero.arrows = arrows + 50
print"Scouts are quick and intelligent, but they are a bit weak.\nTheir prefered weapon is the bow.\n"
break
elsif heroclass == 2
heroclass = "Fighter"
Hero.strength = strength + 3
Hero.charisma = charisma - 3
Hero.weapon = weapon + 3
Hero.life = life + 5
print"Fighters are skilled at close combat, and their endurance is unmached by lesser mortals.\n(They aren't very bright though...)\nTheir prefered weapon is the sword\n"
break
elsif heroclass == 3
heroclass = "Ranger"
Hero.strength = strength - 1
Hero.charisma = charisma + 2
Hero.life = life + 2
Hero.bombs = bombs + 5
Hero.weapon = weapon + 2
print"Rangers are highly skilled, but not as strong as fighters or as agile as scouts.\nThey fight well with all weapons, but they particularly enjoy blowing stuff up.\n"
break
else print "Please enter a valid class number."
end
print "Welcome, #{name}, the #{gender} #{heroclass}, Chosen Hero of the gods!\n"
print "The disembodied voice you are now hearing is your Spirit Guide"
end
When I tried running your code, I got the error
so_question.rb:28:in `<class:Creature>': undefined method `traits' for Creature::Creature:Class (NoMethodError)
from so_question.rb:26:in `<class:Creature>'
from so_question.rb:3:in `<main>'
because you have a class called Creature within the class Creature, and traits is a class method on Creature, not on Creature::Creature.

Resources