0
1

初心者がボウリングゲームをRubyのみで書いてみた

Posted at

はじめに

こんにちは。
一般事務からエンジニアへ転職するために基礎学習しています。
Ruby演習として、VSコード上でRubyのみにて
「Bowling Game ~本日のラッキーボーイ&ガールは一体誰だ!~」
を作成しました。
まだまだひよっこですが、初投稿やってみます。

何を書いたか

ボウリングゲーム の コード (Ruby)

<仕様>

  • 実際の球を投げる行為は、毎回、ランダムな数字が生成および表示されて、それを入力(半角数字)することとした。
    表示されたランダムな数字と入力した数字が異なる場合は、スコアは0となる。
    (スコアを0とさせることで、入力した数字が10以下であるかどうかの判定用コードは作成していない。)
  • ボウリングゲーム のプレイヤーは何人でも可能である。
  • 毎回、ユーザーごとに1投目2投目(10ゲームは3投目がある場合もある)を入力してもらった後、そのゲームまでの表を表示する。
  • 合計スコアが確定していない時は、"..."で表示される。
  • ストライクとスペアの時は、"ナイスストライク!""ナイススペア!"と表示する。(※1)
  • 最後、全員の順位と勝者名が表示される。
  1. ( = ※1 )
    マークの表示切替は今回していない。(マーク:ストライク、スペア、ガーター)
  2. ( = ※2 )
    初心者にとっては、完成までは、コードを書換えして、動作が正しいか確認する時に有効的である。また、ゲームを楽しみたい時にも、コードを書換えして、好きな数字を入力して楽しむこともできる。(途中ズルして高い得点にして、逆転することもできるようになる)

なぜ書いたか

Rubyの基礎学習を終了したため、ボウリングゲーム作成してみました。
WEB検索して、Rubyだけで完結しているボウリングゲームを見つけることができなかったため、記事にします。

今回の目的は、とりあえず完成させることを目的としていました。
コードが長くなってしまいましたが、Rubyのみで作成したい初心者の方の力に少しでもなれたらと思います。

本題

VSコード実行方法

tarminal
ruby ~/ファイル場所/bowling_game.rb

完成図

tarminal
ようこそ BowlingGameへ


このBowlingGameのスコアは全てランダムで決定される運ゲーです

 ※ ここからの数字は、全て半角で入力ください


Let us BowlingGame Start!!

~本日のラッキーボーイ&ガールは一体誰だ!~


プレイヤーの数を入力してください
2

Player1人目の名前を入力してください
エビ

Player2人目の名前を入力してください
カニ

ここから先のスコアは、ランダムで表示されます
表示された数字を入力ください

エビさん 1game 1投目は4です
4

エビさん 1game 2投目は3です
3
+------------------+
|   Bowling_Game   |
+------+-----------+
| name |   game1   |
+------+-----------+
|      |   4 | 3   |
| エビ  | --------- |
|      |     7     |
+------+-----------+

カニさん 1game 1投目は7です
7

カニさん 1game 2投目は1です
1
+------------------+
|   Bowling_Game   |
+------+-----------+
| name |   game1   |
+------+-----------+
|      |   7 | 1   |
| カニ  | --------- |
|      |     8     |
+------+-----------+

エビさん 2game 1投目は10です
10 

ナイスストライク!

+------------------------------+
|         Bowling_Game         |
+------+-----------+-----------+
| name |   game1   |   game2   |
+------+-----------+-----------+
|      |   4 | 3   |  10 | 0   |
| エビ  | --------- | --------- |
|      |     7     |    ...    |
+------+-----------+-----------+

カニさん 2game 1投目は5です
5

カニさん 2game 2投目は2です
2
+------------------------------+
|         Bowling_Game         |
+------+-----------+-----------+
| name |   game1   |   game2   |
+------+-----------+-----------+
|      |   7 | 1   |   5 | 2   |
| カニ  | --------- | --------- |
|      |     8     |    15     |
+------+-----------+-----------+

エビさん 3game 1投目は5です
5

エビさん 3game 2投目は1です
1

.
.
.
.

エビさん 9game 1投目は0です
0

エビさん 9game 2投目は7です
7
+------------------------------------------------------------------------------------------------------------------+
|                                                   Bowling_Game                                                   |
+------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+
| name |   game1   |   game2   |   game3   |   game4   |   game5   |   game6   |   game7   |   game8   |   game9   |
+------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+
|      |   4 | 3   |  10 | 0   |   5 | 1   |   6 | 4   |   6 | 3   |   4 | 0   |   4 | 5   |   9 | 1   |   0 | 7   |
| エビ  | --------- | --------- | --------- | --------- | --------- | --------- | --------- | --------- | --------- |
|      |     7     |    23     |    29     |    45     |    54     |    58     |    67     |    77     |    84     |
+------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+

カニさん 9game 1投目は6です
6

カニさん 9game 2投目は4です
4

ナイススペア!

+------------------------------------------------------------------------------------------------------------------+
|                                                   Bowling_Game                                                   |
+------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+
| name |   game1   |   game2   |   game3   |   game4   |   game5   |   game6   |   game7   |   game8   |   game9   |
+------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+
|      |   7 | 1   |   5 | 2   |   9 | 0   |  10 | 0   |   9 | 0   |   1 | 1   |   7 | 3   |   7 | 0   |   6 | 4   |
| カニ  | --------- | --------- | --------- | --------- | --------- | --------- | --------- | --------- | --------- |
|      |     8     |    15     |    24     |    43     |    52     |    54     |    71     |    78     |    ...    |
+------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+

エビさん 10game 1投目は2です
2

エビさん 10game 2投目は6です
6
+------------------------------------------------------------------------------------------------------------------------------+
|                                                         Bowling_Game                                                         |
+------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+
| name |   game1   |   game2   |   game3   |   game4   |   game5   |   game6   |   game7   |   game8   |   game9   |  game10   |
+------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+
|      |   4 | 3   |  10 | 0   |   5 | 1   |   6 | 4   |   6 | 3   |   4 | 0   |   4 | 5   |   9 | 1   |   0 | 7   | 2 | 6 | 0 |
| エビ  | --------- | --------- | --------- | --------- | --------- | --------- | --------- | --------- | --------- | --------- |
|      |     7     |    17     |    23     |    39     |    48     |    52     |    61     |    71     |    78     |    86     |
+------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+

カニさん 10game 1投目は2です
2

カニさん 10game 2投目は2です
2
+------------------------------------------------------------------------------------------------------------------------------+
|                                                         Bowling_Game                                                         |
+------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+
| name |   game1   |   game2   |   game3   |   game4   |   game5   |   game6   |   game7   |   game8   |   game9   |  game10   |
+------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+
|      |   7 | 1   |   5 | 2   |   9 | 0   |  10 | 0   |   9 | 0   |   1 | 1   |   7 | 3   |   7 | 0   |   6 | 4   | 2 | 2 | 0 |
| カニ  | --------- | --------- | --------- | --------- | --------- | --------- | --------- | --------- | --------- | --------- |
|      |     8     |    15     |    24     |    34     |    43     |    45     |    62     |    69     |    81     |    85     |
+------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+

ゲームエンド!

今回は、エビが勝利しました!
お疲れ様でした。

1位 エビさん  (86点)
2位 カニさん  (85点)



コード

bowling_game.rb

#terminal-tableという名前のGem(Rubyのパッケージ)を読み込んでいる
#Gemがインストールされていない場合は、このコードを実行する前にgem install terminal-tableなどのコマンドを使ってインストールする必要がある
require 'terminal-table'

#users という名前の変数に、空の配列を割り当てる
#この後のコードで 「user <<」 (<<:配列に要素を追加するための演算子)を活用して、配列に要素を追加させていく
users = []


#ゲーム開始時に表示させる文章
puts "ようこそ BowlingGameへ\n\n
このBowlingGameのスコアは全てランダムで決定される運ゲーです\n
 ※ ここからの数字は、全て半角で入力ください\n\n
Let's BowlingGame Start!!\n
~本日のラッキーボーイ&ガールは一体誰だ!~\n\n"


# プレイヤーの数を入力
puts "\nプレイヤーの数を入力してください"
num_players = gets.chomp.to_i

num_players.times do |i|
  puts "\nPlayer#{i+1}人目の名前を入力してください"
  player_name = gets.chomp
  #「user << {}」 配列にハッシュを追加していく
  #ハッシュ(Hash):キーと値のペアを格納するデータ構造。インデックス番号ではなくキーによって要素にアクセスする
  # frame/total_scoreはこの後、配列として使用したいため「[]」で表示させる (total_scoreは最後に勝者を決めるために作成)
  # 今後の配列のイメージ frame = [[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0,0]]
  # 今後の配列のイメージ total_score = [[0,0,0,......0,0]]
  users << { name: player_name , frame: [] , total_score: [] }
end


# プレイ開始
puts "\nここから先のスコアは、ランダムで表示されます\n表示された数字を入力ください\n"

10.times do |j|
  users.each do |user|

    # ランダムな数字を生成して表示
   random_number = rand(0..10)
   puts "\n#{user[:name]}さん #{j+1}game 1投目は#{random_number}です"
   temporary_score1 = gets.chomp.to_i
   
    # 入力した数字が、生成されたランダムな数字と異なる場合、スコアは0とする
    if temporary_score1 != nil
      if temporary_score1 != random_number
        #(※2) score1 = temporary_score1 と入力したら、好きな数字を入力できる
        score1 = 0
        puts "入力に謝りがありましたため、今回のスコアは 0点 です"
      else
        score1 = temporary_score1
      end
    end

    #場合分け  ( j <= 8 /  j == 9 )
    #j == 9の時は、frame内のスコアは3つ発生する
    #j <= 8 : user[:frame] << [score1,score2] 
    #j == 9 : user[:frame] << [score1,score2,score3] 
    if j <= 8

      if score1 == 10
        score2 = 0
        puts "\nナイスストライク!\n\n"

      else
        # 2投目の場合は、10 - score1までの数字で ランダムな数字を生成して表示
        score1_sub = 10 - score1
        random_number = rand(0..score1_sub)
        puts "\n#{user[:name]}さん #{j+1}game 2投目は#{random_number}です"
        temporary_score2 = gets.chomp.to_i

        if temporary_score2 != nil
          if temporary_score2 != random_number
            #(※2) score2 = temporary_score2
            score2 = 0
            puts "入力に謝りがありましたため、今回のスコアは 0点 です"
          else
          score2 = temporary_score2
            if score1 + score2 == 10
              puts "\nナイススペア!\n\n"
            end
          end
        end
      end

      user[:frame] << [score1,score2] 

    elsif j == 9 
      if score1 <= 9

        score1_sub = 10 - score1
        random_number = rand(0..score1_sub)
        puts "\n#{user[:name]}さん #{j+1}game 2投目は#{random_number}です"
        temporary_score2 = gets.chomp.to_i

        if temporary_score2 != nil
          if temporary_score2 != random_number
              #(※2) score2 = temporary_score2
              score2 = 0
              puts "入力に謝りがありましたため、今回のスコアは 0点 です"
          else        
            score2 = temporary_score2
          end

          if score1 + score2 <= 9
            score3 = 0

          elsif score1 + score2 == 10
            puts "\nナイススペア!\nボーナスゲーム!\n\n"
            random_number = rand(0..10)
            puts "\n#{user[:name]}さん #{j+1}game 3投目は#{random_number}です"
            temporary_score3 = gets.chomp.to_i
            if temporary_score3 != nil
              if temporary_score3 != random_number
                #(※2) score3 = temporary_score3
                score3 = 0
                puts "入力に謝りがありましたため、今回のスコアは 0点 です"
              else
                score3 = temporary_score3
              end
            end
          end                   
        end

      elsif score1 == 10
        puts "\nナイスストライク!\nボーナスゲーム!\n\n"
        random_number = rand(0..10)
        puts "\n#{user[:name]}さん #{j+1}game 2投目は#{random_number}です"
        temporary_score2 = gets.chomp.to_i

        if temporary_score2 != nil
          if temporary_score2 != random_number
              #(※2) score2 = temporary_score2
              score2 = 0
              puts "入力に謝りがありましたため、今回のスコアは 0点 です"
          else
            score2 = temporary_score2
          end

          if  score2 <= 9
            score2_sub = 10 - score2
            random_number = rand(0..score2_sub)
            puts "\n#{user[:name]}さん #{j+1}game 3投目は#{random_number}です"
            temporary_score3 = gets.chomp.to_i
            if temporary_score3 != nil
              if temporary_score3 != random_number
                #(※2) score3 = temporary_score3
                score3 = 0
                puts "入力に謝りがありましたため、今回のスコアは 0点 です"
              else
                score3 = temporary_score3
              end
            end

          elsif score2 == 10
            puts "\nナイスストライク!\nボーナスゲーム!\n\n"
            random_number = rand(0..10)
            puts "\n#{user[:name]}さん #{j+1}game 3投目は#{random_number}です"
            temporary_score3 = gets.chomp.to_i

            if temporary_score3 != nil
              if temporary_score3 != random_number
                #(※2) score3 = temporary_score3
                score3 = 0
                puts "入力に謝りがありましたため、今回のスコアは 0点 です"
              end
            else
                score3 = temporary_score3
            end
          end          
          
        end
      end
      user[:frame] << [score1,score2,score3] 
    end


    #表を作成していく
    row_data = [] #row_data という名前の変数に、空の配列を割り当てる
    frame_before_total = 0  # 前のフレームまでの合計スコアを保持する変数
    total_score = 0  # 最終的な合計スコアを保持する変数
    
  
    #\nを使用して文字が真ん中に来るようにしている
    row_data << "\n#{user[:name]}\n\n"
    #user[:frame].each_with_index do |frame,index|内で、
    #毎回配列が作成される為(毎回全てのindex時の表が作成されることになる)、
    #内側にtemporary_table(一時的なtable)を作成して、最後、「table = temporary_table」とすることで
    #一番最後のindex時のみがtableとして反映されるようにしている
    table = Terminal::Table.new
    
      user[:frame].each_with_index do |frame,index|
        framebefore = index >0 ? frame_before_total : 0 #index==0の時は、0となる
        framenow = frame.sum
        bounus = 0 #ストライクやスペアが出た時の次のフレームの加算点 (初期値は0)
        non_frame_total = 0 #合計が確定していない時は、"..."と表示するため、初期値は0としておく

        if index <= 8 || (index == 9 && user[:frame].length <= 9)
          frame_next = user[:frame].length > 0 ? user[:frame][index+1] : nil       
          frame_next_after_next = user[:frame].length > 1 ? user[:frame][index+2] :  nil

          
            if frame.sum == 10 && frame[0] == 10
              if user[:frame].length == 10 && index >= 8
                bounus += frame_next[0] + frame_next[1]

              elsif user[:frame].length <= 9
                bounus += frame_next.sum if frame_next
                non_frame_total = "..." unless frame_next

                if frame_next && frame_next.sum == 10 && frame_next[0] == 10                 
                bounus += frame_next_after_next[0] if frame_next_after_next
                non_frame_total = "..." unless frame_next_after_next
                end
              end
            
            elsif
              frame.sum == 10
              bounus += frame_next[0] if frame_next
              non_frame_total = "..." unless frame_next
            end

            # non_frame_totalを初期値0と設定したため、
            #その後に、"..."へ上書きされていなければ、frame_total を計算する
            frame_total = non_frame_total != 0? non_frame_total : framenow + framebefore + bounus

            # スコアの表示方法 2フレーム目以降は横に並べる
          if index == 0
            row_data << "#{frame[0]} | #{frame[1]}\n---------\n#{frame_total}"
          else
            row_data << "#{frame[0]} | #{frame[1]}\n---------\n#{frame_total}"
          end
    
            # 前のフレームまでの合計スコアを更新
            frame_before_total = frame_total
            temporary_total_score = frame_total

        elsif index == 9 && user[:frame].length == 10
            
          # 10ゲーム目の時は、次のフレームに対するbounusは発生しない
          frame_total = framenow + framebefore
          # スコアの表示方法 2フレーム目以降は横に並べる(10ゲーム目は3フレームを横に並べる)
          row_data << "#{frame[0]} | #{frame[1]} | #{frame[2]}\n---------\n#{frame_total}"    
          temporary_total_score = frame_total
  
        end

        # テーブルの作成と表示
        temporary_table = Terminal::Table.new do |t|
          t.title = "Bowling_Game"
          t.rows = [row_data]
          t.headings = ['name'] + (0..index).map { |i| "game#{i+1}" }

          # 全体の中央揃えを設定
          t.style = {
            all_separators: true,
            alignment: :center }
      end
      
      table = temporary_table
      user[:total_score] << temporary_total_score
   end
      
      puts table


  end

end

#結果発表
#スコアの順位順に並び替え users(変数)の中の total_scoreの配列の最後の値で降順(-w:降順/w:昇順)
#降順となるため、1位の人は、sorted_user_scores[0]が該当する
sorted_user_scores = users.sort_by { |w| -w[:total_score].last }
puts  "\nゲームエンド!\n\n今回は、#{sorted_user_scores[0][:name]}が勝利しました!\nお疲れ様でした。\n\n"

#順位発表
#total_scoreの配列の最後の値を表示したいため、「.last」必要
sorted_user_scores.each_with_index do |w,index|
  puts "#{index+1}#{w[:name]}さん  (#{w[:total_score].last}点)"
end


最後に

最後まで読んでいただきありがとうございました!

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1