Class: Deck

Inherits:
Array show all
Defined in:
lib/quiz1/t/solutions/Bill Guindon/solitaire.rb,
lib/quiz1/t/solutions/Jamis Buck/lib/cipher.rb,
lib/quiz1/t/solutions/Dennis Ranke/solitaire.rb,
lib/quiz1/t/solutions/Glen M. Lewis/solitaire.rb,
lib/quiz1/t/solutions/Thomas Leitner/solitaire.rb,
lib/quiz1/t/solutions/Niklas Frykholm/solitaire.rb,
lib/quiz1/t/solutions/Jim Menard/solitaire_cypher.rb

Overview

Handles the deck

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from Array

#collect_peel, #generate_keystream, #next_key, #peel, #wrap_down

Constructor Details

#initializeDeck

Initializes the deck with the default values



10
11
12
13
# File 'lib/quiz1/t/solutions/Thomas Leitner/solitaire.rb', line 10

def initialize
  @deck = (1..52).to_a + [ "A", "B" ]
  @length = @deck.length
end

Instance Attribute Details

#cardsObject

Returns the value of attribute cards.



13
14
15
# File 'lib/quiz1/t/solutions/Bill Guindon/solitaire.rb', line 13

def cards
  @cards
end

#orderObject (readonly)

Returns the value of attribute order.



13
14
15
# File 'lib/quiz1/t/solutions/Bill Guindon/solitaire.rb', line 13

def order
  @order
end

Instance Method Details

#add_card(name, suit, rank) ⇒ Object

build order while adding.



40
41
42
43
44
# File 'lib/quiz1/t/solutions/Bill Guindon/solitaire.rb', line 40

def add_card(name, suit, rank)
  card = Card.new(name, suit, rank)
  @cards << card
  @order << card.to_s
end

#build_deckObject



25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/quiz1/t/solutions/Bill Guindon/solitaire.rb', line 25

def build_deck
  [:Clubs, :Diamonds, :Hearts, :Spades].each do |suit|
    rank = 0
    'A23456789TJQK'.each_byte do |name|
      # 'real' cards have a rank value
      rank += 1
      add_card(name.chr, suit, rank)
    end
  end

  # Jokers have no rank value
  'AB'.each_byte {|name| add_card(name.chr, :Joker, 0)}
end

#cipher_letterObject



32
33
34
35
36
37
38
39
# File 'lib/quiz1/t/solutions/Jamis Buck/lib/cipher.rb', line 32

def cipher_letter
  count = @deck.first
  count = 53 if count.is_a?( String )
  result = @deck[ count ]
  return nil unless result.is_a? Fixnum
  result -= 26 while result > 26
  return (result+64).chr
end

#cipher_shuffle!Object



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/quiz1/t/solutions/Jamis Buck/lib/cipher.rb', line 10

def cipher_shuffle!
  # move joker A down one card, circularly
  reposition_card( "A", 1 )

  # move joker B down two cards, circularly
  reposition_card( "B", 2 )

  joker_A = @deck.index( "A" )
  joker_B = @deck.index( "B" )

  # move all cards above the top-most joker, below the bottom-most joker, and
  # all cards below the bottom-most joker, above the top-most joker.
  top    = ( joker_A < joker_B ? joker_A : joker_B )
  bottom = ( joker_A > joker_B ? joker_A : joker_B )
  @deck = @deck[bottom+1..-1] + @deck[top..bottom] + @deck[0,top]

  # take value of the bottom-most card, and cut that many cards off the
  # top, inserting them just before the bottom-most card.
  cut = @deck.last
  @deck = @deck[cut..-2] + @deck[0,cut] + [ @deck.last ]
end

#count_cutObject

Perform a count cut using the value of the bottom card. Cut the bottom card’s value in cards off the top of the deck and reinsert them just above the bottom card.



33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/quiz1/t/solutions/Thomas Leitner/solitaire.rb', line 33

def count_cut
  print "before count_cut: " if $debug
  self.dump if $debug
  temp = self.dup
  self.clear
  num = temp[-1].value
  temp.slice(num..-2).each {|x| self.push(x) }
  temp.slice(0..(num-1)).each {|x| self.push(x) }
  self.push(temp[-1])
  print "after count_cut: " if $debug
  self.dump if $debug
end

#create_keystream(count) ⇒ Object



6
7
8
9
10
11
12
13
14
# File 'lib/quiz1/t/solutions/Dennis Ranke/solitaire.rb', line 6

def create_keystream(count)
  stream = []
  count.times do
    letter = next_letter
    redo unless letter
    stream << letter
  end
  return stream
end

#cut_cards(cuts) ⇒ Object

does as many cuts as you give it.



52
53
54
55
56
57
58
59
60
# File 'lib/quiz1/t/solutions/Bill Guindon/solitaire.rb', line 52

def cut_cards(cuts)
  cards = []
  loc = 0
  [cuts].flatten.each_with_index do |cut, idx|
    cards[idx] = @cards[loc...cut]
    loc = cut
  end
  cards << @cards[loc...@cards.length]
end

#dumpObject



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/quiz1/t/solutions/Glen M. Lewis/solitaire.rb', line 64

def dump
  self.each do |c|
    if (c.value == 53)
      print c.face_value
    else
	print c.value
    end
    print " "
  end
  print "\n\n"
  if (@deck_size != self.size) then
    puts "ERROR!  Deck size changed to #{self.size}"
    exit
  end
end

#find_card(name, suit) ⇒ Object

Uses order to hunt for cards (Joker searches).



47
48
49
# File 'lib/quiz1/t/solutions/Bill Guindon/solitaire.rb', line 47

def find_card(name, suit)
  return @order.index(Card.to_s(name, suit))
end

#find_joker(j) ⇒ Object



80
81
82
83
84
85
86
87
# File 'lib/quiz1/t/solutions/Glen M. Lewis/solitaire.rb', line 80

def find_joker(j)
  self.each_index do |i|
    if (self[i].face_value == j)
	return i
    end
  end
  puts "ERROR: Could not find joker '#{j}' in deck."
end

#generate_next_keystream_valueObject

Return the next keystream value as a number 1-26 (not a string).



75
76
77
78
79
80
81
# File 'lib/quiz1/t/solutions/Jim Menard/solitaire_cypher.rb', line 75

def generate_next_keystream_value
	move(@joker_a, 1)
	move(@joker_b, 2)
	triple_cut()
	count_cut()
	return output_number()
end

#getObject



61
62
63
64
65
66
67
68
69
70
# File 'lib/quiz1/t/solutions/Niklas Frykholm/solitaire.rb', line 61

def get
  while true
    update
    c = output
    if c != :A and c != :B
      letter = ( ((c-1) % 26) + 65 ).chr
      return letter
    end
  end
end

#keyObject

Keys the deck and returns itself.



59
60
61
62
# File 'lib/quiz1/t/solutions/Jim Menard/solitaire_cypher.rb', line 59

def key
	# do nothing; keyed when initialized
	self
end

#keystream_message(msg) ⇒ Object



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/quiz1/t/solutions/Glen M. Lewis/solitaire.rb', line 140

def keystream_message(msg)
  # result = "DWJXHYRFDGTMSHPUURXJ"
  result = ""
  while (result.length < msg.length) do
    # Step 2 - Move the A Joker down one card
    pos = find_joker("AJoker")
    move_card_down(pos, 1)
    # Step 3 - Move the B Joker down two cards
    pos = find_joker("BJoker")
    move_card_down(pos, 2)
    # Step 4 - Triple cut split around two jokers
    apos = find_joker("AJoker")
    bpos = find_joker("BJoker")
    triple_cut_split(apos, bpos)
    # Step 5 - Count cut
    count_cut
    # Step 6 - Output letter - might be nil
    letter = output_letter
    result << letter if letter
  end
  return result
end

#letter_at(index) ⇒ Object



74
75
76
77
# File 'lib/quiz1/t/solutions/Dennis Ranke/solitaire.rb', line 74

def letter_at(index)
  id = @deck[index]
  (id > 51) ? nil : (id % 26) + 1
end

#move(card, distance) ⇒ Object

Move a card a certain distance. Wrap around the end of the deck.



84
85
86
87
88
89
90
# File 'lib/quiz1/t/solutions/Jim Menard/solitaire_cypher.rb', line 84

def move(card, distance)
	old_pos = @cards.index(card)
	new_pos = old_pos + distance
	new_pos -= (@cards.length-1) if new_pos >= @cards.length
	@cards[old_pos,1] = []
	@cards[new_pos,0] = [card]
end

#move_aObject



15
16
17
# File 'lib/quiz1/t/solutions/Niklas Frykholm/solitaire.rb', line 15

def move_a
  move_down(@deck.index(:A))
end

#move_AObject

Operation “move a” (step 2)



15
16
17
# File 'lib/quiz1/t/solutions/Thomas Leitner/solitaire.rb', line 15

def move_A
  move_down( @deck.index( 'A' ) )
end

#move_bObject



19
20
21
# File 'lib/quiz1/t/solutions/Niklas Frykholm/solitaire.rb', line 19

def move_b
  move_down(move_down(@deck.index(:B)))
end

#move_BObject

Operation “move b” (step 3)



20
21
22
# File 'lib/quiz1/t/solutions/Thomas Leitner/solitaire.rb', line 20

def move_B
  2.times { move_down( @deck.index( 'B' ) ) }
end

#move_card_down(pos, num) ⇒ Object



89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/quiz1/t/solutions/Glen M. Lewis/solitaire.rb', line 89

def move_card_down(pos, num)
  print "before move_card_down(#{pos}, #{num}): " if $debug
  self.dump if $debug
  dest = pos + num
  dest -= (self.size-1) if (dest >= self.size)
  card = self.delete_at(pos)
  temp = self.dup
  self.clear
  temp.slice(0, dest).each {|x| self.push(x) }
  self << card
  temp.slice(dest..(-1)).each {|x| self.push(x) }
  print "after move_card_down(#{pos}, #{num}): " if $debug
  self.dump if $debug
end

#move_down(index) ⇒ Object

Moves the index one place down while treating the used array as circular list.



78
79
80
81
82
83
84
85
# File 'lib/quiz1/t/solutions/Thomas Leitner/solitaire.rb', line 78

def move_down( index )
  if index == @deck.length - 1
    @deck[1..1] = @deck[index], @deck[1]
    @deck.pop
  else
    @deck[index], @deck[index + 1] = @deck[index + 1], @deck[index]
  end
end

#next_keystreamObject

Return the next kestream value as a number (not a string). Keep going until we have a non-joker value.



66
67
68
69
70
71
72
# File 'lib/quiz1/t/solutions/Jim Menard/solitaire_cypher.rb', line 66

def next_keystream
	val = JOKER_VALUE
	until val != JOKER_VALUE
 val = generate_next_keystream_value
	end
	val
end

#next_letterObject



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/quiz1/t/solutions/Dennis Ranke/solitaire.rb', line 16

def next_letter
  ##
  # move the jokers
  ##

  2.times do |j|
    # find the joker
    index = @deck.index(52 + j)

    # remove it from the deck
    @deck.delete_at(index)

    # calculate new index
    index = ((index + j) % 53) + 1

    # insert the joker at that index
    @deck[index, 0] = 52 + j
  end

  ##
  # do the tripple cut
  ##

  # first find both jokers
  a = @deck.index(52)
  b = @deck.index(53)

  # sort the two indeces
  low, hi = [a, b].sort

  # get the lower and upper parts of the deck
  upper = @deck.slice!((hi + 1)..-1)
  lower = @deck.slice!(0, low)

  # swap them
  @deck = upper + @deck + lower

  ##
  # do the count cut
  ##

  # find out the number of cards to cut
  count = value_at(53)

  # remove them from the top of the deck
  cards = @deck.slice!(0, count)

  # reinsert them just above the lowest card
  @deck[-1, 0] = cards

  return letter_at(value_at(0))
end

#number_value(x) ⇒ Object



40
41
42
43
# File 'lib/quiz1/t/solutions/Niklas Frykholm/solitaire.rb', line 40

def number_value(x)
  return 53 if x == :A or x == :B
  return x
end

#outputObject



50
51
52
# File 'lib/quiz1/t/solutions/Niklas Frykholm/solitaire.rb', line 50

def output
  return @deck[ number_value(@deck[0]) ]
end

#output_letterObject

Operation “output the found letter” (step 6)



40
41
42
43
44
45
46
47
48
# File 'lib/quiz1/t/solutions/Thomas Leitner/solitaire.rb', line 40

def output_letter
  num = self[0].value
  card = self[num]
  return nil if (card.value == 53)
  num = (card.value > 26 ? card.value-26 : card.value)
  char = (num-1 + "A"[0]).chr
  puts "card.value=#{card.value}, char=#{char}" if $debug
  return char
end

#output_numberObject

Return the output number (not letter). Convert the top card to it’s value and count down that many cards from the top of the deck, with the top card itself being card number one. Look at the card immediately after your count and convert it to a letter. This is the next letter in the keystream. If the output card is a joker, no letter is generated this sequence. This step does not alter the deck.



122
123
124
125
126
127
128
# File 'lib/quiz1/t/solutions/Jim Menard/solitaire_cypher.rb', line 122

def output_number
	i = @cards[0].to_i
	i -= @cards.length if i >= @cards.length
	num = @cards[i].to_i
	num -= 26 if num > 26
	num
end

#shuffle(init, method = :fisher_yates) ⇒ Object

Shuffle the deck using the initialization number init and the method method. Currently there are only two methods: :fisher_yates and naive.



54
55
56
57
# File 'lib/quiz1/t/solutions/Thomas Leitner/solitaire.rb', line 54

def shuffle( init, method = :fisher_yates )
  srand( init )
  self.send( method.id2name + "_shuffle", @deck )
end

#slice(from, to) ⇒ Object

Returns a non-nil cut of cards from the deck.



111
112
113
114
# File 'lib/quiz1/t/solutions/Jim Menard/solitaire_cypher.rb', line 111

def slice(from, to)
	slice = @cards[from..to]
	return slice || []
end

#to_aObject



41
42
43
# File 'lib/quiz1/t/solutions/Jamis Buck/lib/cipher.rb', line 41

def to_a
  @deck.dup
end

#to_sObject



21
22
23
# File 'lib/quiz1/t/solutions/Bill Guindon/solitaire.rb', line 21

def to_s
  return @cards.to_s
end

#triple_cutObject

Perform a triple cut around the two jokers. All cards above the top joker move to below the bottom joker and vice versa. The jokers and the cards between them do not move.



25
26
27
28
29
30
# File 'lib/quiz1/t/solutions/Thomas Leitner/solitaire.rb', line 25

def triple_cut
  a = @deck.index( 'A' )
  b = @deck.index( 'B' )
  a, b = b, a if a > b
  @deck.replace( [@deck[(b + 1)..-1], @deck[a..b], @deck[0...a]].flatten )
end

#triple_cut_split(a, b) ⇒ Object



104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/quiz1/t/solutions/Glen M. Lewis/solitaire.rb', line 104

def triple_cut_split(a, b)
  a,b=b,a if (a > b)
  print "before triple_cut_split(#{a}, #{b}): " if $debug
  self.dump if $debug
  temp = self.dup
  self.clear
  temp.slice((b+1)..-1).each {|x| self.push(x) }
  temp.slice(a..b).each {|x| self.push(x) }
  temp.slice(0..(a-1)).each {|x| self.push(x) }
  print "after triple_cut_split(#{a}, #{b}): " if $debug
  self.dump if $debug
end

#updateObject



54
55
56
57
58
59
# File 'lib/quiz1/t/solutions/Niklas Frykholm/solitaire.rb', line 54

def update
  move_a
  move_b
  triple_cut
  count_cut
end

#update_orderObject

simple, but not very efficient.



70
71
72
# File 'lib/quiz1/t/solutions/Bill Guindon/solitaire.rb', line 70

def update_order
  @order = @cards.collect {|card| card.to_s}
end

#value_at(index) ⇒ Object



69
70
71
72
# File 'lib/quiz1/t/solutions/Dennis Ranke/solitaire.rb', line 69

def value_at(index)
  id = @deck[index]
  (id > 51) ? 53 : id + 1
end