チンチロプログラムのリファクタリングをしました。
先日作成したチンチロプログラムのリファクタリングを行いました。
前回作成時点では、
「モノは動くけどクラス定義やメソッドがこれでいいのか、全然わかんない!」状態でした。
現在通っているプログラミングスクール講師の方にコードレビューをしていただき、
特に正しいクラス定義、メソッド定義の仕方について理解した上で実装できるようになりました。
リファクタリング前後のコードを見比べると明らかに分かりやすく、かつ
保守性の高いコードになったかなと思います。
やっぱり他の人に見てもらうって大事ですねえ。感謝。
機能追加
機能追加しました。
① プレイヤー人数設定(2~4人)
② ゲーム終了判定
③ 賭け金の入力制限
リファクタリング後に機能追加したのですが、
「嗚呼!これがオブジェクト指向かっ!!」と、その保守性の高さを身をもって経験できました🤗
main.rb
# チンチロ for 2~4player
# ●ルール
# --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
# ゲームは2〜4人で行われ、まずは親を決める。親以外のメンバーは子になる
# 子が賭け金を決めたら、親がサイコロを振って勝負する役(数)を決める。3回まで振ることができるが、役が出たらその役で勝負
# 子が一人ずつサイコロを振る。親より強い役が出たら子の勝ち。同じなら親の勝ち。親より弱いのが出たら子の負け。親に勝ったら子が賭けた金額を親からもらい、負けたら親に払う。
# 役によってもらえる賭け金が増減する(下記参照)。ex) 子:ピンゾロ 親:ヒフミ => 子が賭け金の10倍もらえる
# 子全員との勝負が終わったら親の交代で、左隣の人に親の権利が移る。
# これを繰り返して参加者全員に親が回ったら、一回のゲームが終了。
# --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
# ● 役(強い順)
# --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
# 1.ピンゾロ : 111 賭け金の5倍貰える
# 2.アラシ. : 222,333,444,555,666 賭け金の3倍貰える
# 3.シゴロ. : 456 賭け金の2倍貰える
# 4.出目 : 同じ数字2つ+別の数字 ex) 116 → 出目6 551 → 出目1 賭け金の1倍貰える
# 5.目ナシ. : 他の役以外 賭け金の1倍払う
# 6.ヒフミ. : 123 賭け金の2倍払う
# --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
require "./tintiro_game_manager.rb"
DELIMITER = '----------------------------------------'
DELIMITER_STAR = '★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★'
DEFAULT_MONEY = 50000
ROLE = { parent: '親', child: '子'}
roles = [ROLE[:parent], ROLE[:child], ROLE[:child], ROLE[:child]]
tintiro = TintiroGameManager.new
#タイトル出力
TintiroGameManager.output_title
#プレイヤー人数入力(2~4人)
tintiro.input_player_num
#プレイヤー名前入力
tintiro.input_player_name
#User作成
tintiro.create_users(roles)
#所持金表示
tintiro.output_possession_meney
play_count = 1
while play_count <= tintiro.player_num
#親子/掛け金決め
tintiro.decide_role_and_betmoney
# 子のターン
tintiro.child_turn
# 親のターン
tintiro.parent_turn
#結果出力
tintiro.output_result
#所持金表示
tintiro.output_possession_meney
#ゲーム終了判定
break if tintiro.determine_game_end?(play_count)
#親子入れ替え、掛け金初期化
tintiro.reset_user(roles)
sleep(2)
play_count += 1
end
tintiro_game_manager.rb
ゲーム進行に関わるクラス
require "./user.rb"
require "./hand.rb"
class TintiroGameManager
attr_reader :player_num
def initialize
end
#タイトル出力
def self.output_title
print <<~EOS
#{DELIMITER_STAR}
★ チンチロ ★
#{DELIMITER_STAR}
EOS
end
#プレイヤー人数入力(2~4人)
def input_player_num
print "何人で遊びますか?(2~4人) : "
@player_num = gets.to_i
while (@player_num < 2) || (@player_num > 4)
puts '2~4を入力してください'
@player_num = gets.to_i
end
output_DELIMITER
end
#プレイヤー名前入力
def input_player_name
@player_names = []
@player_num.times { |i|
print "名前(#{i + 1}人目)を入力してください : "
@player_names[i] = gets.to_s.chomp
}
end
#User作成
def create_users(roles)
@players = []
@register_order_players = []
@player_num.times { |i|
@players[i] = User.new(roles[i], @player_names[i])
@register_order_players[i] = @players[i]
}
end
#所持金を表示する
def output_possession_meney
output_DELIMITER
puts '<所持金>'
@register_order_players.each { |player|
player.show_possession_money
}
output_DELIMITER
end
#親子/賭け金決め
def decide_role_and_betmoney
puts "★★★★★★★★★★ #{ROLE[:parent]}は「#{@players[0].name}」です ★★★★★★★★★★"
output_DELIMITER
puts "● #{ROLE[:child]}の賭け金を決めます。"
@players.each_with_index { |player, i|
next if i == 0 #親は賭けないので飛ばす
player.set_bet_money(player)
puts "→ #{player.name}は#{player.bet_money}ソシー賭けました"
output_DELIMITER
}
#子と親で配列分割
@childs = @players.last(@player_num - 1)
@parent = @players.first(1)
end
#子のターン
def child_turn
@child_hands = []
@childs.each_with_index { |child, i|
@child_hands[i] = Hand.new
child.play_child(@child_hands[i])
}
end
#親のターン
def parent_turn
@parent_hand = Hand.new
@parent[0].play_parent(@parent_hand)
end
#結果出力
def output_result
@childs.zip(@child_hands) { |child, child_hand|
#勝敗判定
determine_win_lose(child, child_hand, @parent, @parent_hand)
#賞金計算
calc_award(child)
}
end
#ゲーム終了判定
def determine_game_end?(play_count)
#プレイヤー全員に親が回ったらゲーム終了
if play_count == @player_num
puts '全員に親が回ったのでゲームを終了します。'
output_DELIMITER
return true
elsif play_count < @player_num
#所持金不足によるゲーム終了判定
if money_shortage?
output_DELIMITER
true
end
end
end
#親子入れ替え、賭け金を初期化
def reset_user(roles)
@players << @players.shift #playerの順番を回す
@players.each_with_index { |player, i|
player.bet_money = 0
player.role = roles[i]
}
puts '● 親子を交換します。'
output_DELIMITER
end
# ----------------------------------- private -------------------------------------------
private
def output_DELIMITER
puts DELIMITER
end
# 勝敗判定
def determine_win_lose(child, child_hand, parent, parent_hand)
# 役が出目同士のとき
if parent_hand.hand_info[:hand_num] == 4 && child_hand.hand_info[:hand_num] == 4
if parent_hand.hand_info[:deme_num][0] >= child_hand.hand_info[:deme_num][0]
@winner = parent[0]
@loser = child
else
@winner = child
@loser = parent[0]
end
# 役が出目同士以外のとき
elsif parent_hand.hand_info[:hand_num] <= child_hand.hand_info[:hand_num]
@winner = parent[0]
@loser = child
else
@winner = child
@loser = parent[0]
end
output_DELIMITER
puts "#{@winner.name}(#{@winner.role})の勝ち!"
@bet_money = child.bet_money
end
#賞金計算
def calc_award(child)
case @winner.hand[:hand_num]
when 1 #ピンゾロ
award_money = child.bet_money * 5
when 2 #アラシ
award_money = child.bet_money * 3
when 3 #シゴロ
award_money = child.bet_money * 2
else #出目/目ナシ
award_money = child.bet_money * 1
end
award_money *= 2 if @loser.hand[:hand_num] == 6 #負けた方がヒフミの場合、賞金2倍
puts "#{@winner.name}(#{@winner.role})が、#{award_money}ソシー獲得しました!"
@winner.possession_money += award_money
@loser.possession_money -= award_money
end
#所持金不足によるゲーム終了判定
def money_shortage?
end_flag = 0
@players.each { |player|
if player.possession_money <= 0
puts "#{player.name}の所持金が無くなったためゲームを終了します。"
end_flag = 1
end
}
if end_flag == 1
return true
end
end
end
user.rb
ユーザーに関するクラス
require "./hand.rb"
class User
attr_accessor :role, :possession_money, :bet_money
attr_reader :hand, :name
def initialize(role, player_name)
@role = role
@name = player_name
@bet_money = 0
@possession_money = DEFAULT_MONEY
end
# ユーザーの所持金を表示する
def show_possession_money
name_length10 = ['%10s']
name_length10 = @name.ljust(10)
puts "#{name_length10}: #{@possession_money}ソシー"
end
#賭け金設定
def set_bet_money(player)
puts "・#{player.name}は「#{player.role}」です。"
print '賭け金を決めてください(ソシー) : '
while true
@bet_money = gets.to_i
# @bet_money = 100
if @bet_money > player.possession_money
puts '賭け金が足りません。再度入力してください。'
next
elsif @bet_money <= 0
puts '賭け金は0以上にしてください。再度入力してください。'
next
end
break
end
end
# 子ターン
def play_child(child_hand)
throw_dice(child_hand)
child_hand.output_hand(@role, @name)
end
# 親ターン
def play_parent(parent_hand)
throw_dice(parent_hand)
parent_hand.output_hand(@role, @name)
end
#親判定
def parent?
role == ROLE[:parent]
end
# ----------------------------------- private -------------------------------------------
private
#サイコロを振る
def throw_dice(hand)
sleep(1)
puts "★★★★★★★★★★ #{@name}(#{@role})のターン ★★★★★★★★★★"
play_count = 1
while (play_count <= 3) && hand.hand_info[:hand_num] == 5
dices = Array.new(3).map{rand(1..6)}
puts "● #{play_count}投目"
dices.each do |dice|
print "#{dice} "
sleep(0.5)
end
print "\n"
@hand = hand.get_hand(dices)
play_count += 1
end
end
end
hand.rb
役に関するクラス
class Hand
attr_reader :hand_info
def initialize
@hand_info = {hand_name: '', hand_num: 5, deme_num: 0}
end
#役を判定し、取得する
def get_hand(dices)
if (dices[0] == dices[1]) && (dices[1] == dices[2])
@hand_info[:hand_name] = 'アラシ'
@hand_info[:hand_num] = 2
if dices.sum == 3
@hand_info[:hand_name] = 'ピンゾロ'
@hand_info[:hand_num] = 1
end
elsif (dices.sum >= 15) && (dices.include?(5))
@hand_info[:hand_name] = 'シゴロ'
@hand_info[:hand_num] = 3
elsif dices.sum == 6
@hand_info[:hand_name] = 'ヒフミ'
@hand_info[:hand_num] = 6
else
@hand_info[:hand_name] = '目ナシ'
@hand_info[:hand_num] = 5
end
#出目判定
1.upto(6){ |n|
# 2/3のサイコロが同じ目だった場合
if dices.select{ |m| m == n}.size == 2
@hand_info[:hand_name] = '出目'
@hand_info[:hand_num] = 4
@hand_info[:deme_num] = dices.reject{ |k| k == n}
break
end
}
if @hand_info[:hand_num] == 5
puts '目ナシ...'
end
@hand_info
end
#役を出力する
def output_hand(role, name)
if @hand_info[:hand_num] == 4
puts "#{name}(#{role})の結果 : #{@hand_info[:hand_name]}(#{@hand_info[:deme_num][0]})"
else
puts "#{name}(#{role})の結果 : #{@hand_info[:hand_name]}"
end
end
end
最後に
プログラミング楽しい。時間が瞬で溶けていく🫠