保龄球计分
这篇博文分享下保龄球计分算法。
计分规则描述如下:
A game of tenpins bowling lasts ten frames, in each of which the bowler makes one or two attempts to knock down ten pins arranged in a triangle. If the bowler knocks down all ten pins on the first attempt (that’s called a “strike”), he scores ten pins plus the number of pins knocked down on his next two rolls. If the bowler knocks down all ten pins after two attempts (that’s called a “spare”), he scores ten pins plus the number of pins knocked down on his next roll. If the bowler fails to knock down all ten pins (that’s called an “open frame”), he scores the number of pins he knocked down. The scores accumulate through all ten frames. At the last frame, if necessary, the pins are reset for one or two additional rolls to count the final bonus. The sample scoresheet below shows how the calculations work:
For instance, the score in the second frame is 9, the sum of the two balls in the open frame. The score in the third frame is 15, which is 10 for the spare plus 5 on the next ball. The score in the ninth frame is 20, which is 10 for the strike plus 10 for the spare on the first two balls of the tenth frame. The score in the tenth frame is 16, which is 10 for the spare plus 6 for the extra ball, which is a bonus ball not really part of any frame (the two balls of the tenth frame have already been rolled).
下面实现这个需求,用WPF框架来实现。
程序的总体结构如下:
其中,第二个工程为对应的单元测试。
MainWindow设计如下:
MainWindow.xaml和MainWindow.xaml.cs如下:
<Window x:Class="BowlingKate.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Bowling Kate" Height="347.898" Width="967.493"> <StackPanel> <StackPanel Orientation="Horizontal" > <GroupBox Header="第1轮" > <GroupItem> <StackPanel> <StackPanel Orientation="Horizontal"> <TextBox x:Name="Frame1_1" Width="40" Height="40" FontSize="30" TextAlignment="Center" Background="LightCyan"/> <TextBox x:Name="Frame1_2" Width="40" Height="40" FontSize="30" TextAlignment="Center" Background="LightCyan"/> </StackPanel> <TextBlock Text="本轮得分" Background="LightPink"/> <TextBlock x:Name="Frame1_Score" Width="80" Height="40" FontSize="30" TextAlignment="Center" Background="LightGreen" /> </StackPanel> </GroupItem> </GroupBox> <GroupBox Header="第2轮" > <GroupItem> <StackPanel> <StackPanel Orientation="Horizontal"> <TextBox x:Name="Frame2_1" Width="40" Height="40" FontSize="30" TextAlignment="Center" Background="LightCyan"/> <TextBox x:Name="Frame2_2" Width="40" Height="40" FontSize="30" TextAlignment="Center" Background="LightCyan"/> </StackPanel> <TextBlock Text="本轮得分" Background="LightPink"/> <TextBlock x:Name="Frame2_Score" Width="80" Height="40" FontSize="30" TextAlignment="Center" Background="LightGreen" /> </StackPanel> </GroupItem> </GroupBox> <GroupBox Header="第3轮" > <GroupItem> <StackPanel> <StackPanel Orientation="Horizontal"> <TextBox x:Name="Frame3_1" Width="40" Height="40" FontSize="30" TextAlignment="Center" Background="LightCyan"/> <TextBox x:Name="Frame3_2" Width="40" Height="40" FontSize="30" TextAlignment="Center" Background="LightCyan"/> </StackPanel> <TextBlock Text="本轮得分" Background="LightPink"/> <TextBlock x:Name="Frame3_Score" Width="80" Height="40" FontSize="30" TextAlignment="Center" Background="LightGreen" /> </StackPanel> </GroupItem> </GroupBox> <GroupBox Header="第4轮" > <GroupItem> <StackPanel> <StackPanel Orientation="Horizontal"> <TextBox x:Name="Frame4_1" Width="40" Height="40" FontSize="30" TextAlignment="Center" Background="LightCyan"/> <TextBox x:Name="Frame4_2" Width="40" Height="40" FontSize="30" TextAlignment="Center" Background="LightCyan"/> </StackPanel> <TextBlock Text="本轮得分" Background="LightPink"/> <TextBlock x:Name="Frame4_Score" Width="80" Height="40" FontSize="30" TextAlignment="Center" Background="LightGreen" /> </StackPanel> </GroupItem> </GroupBox> <GroupBox Header="第5轮" > <GroupItem> <StackPanel> <StackPanel Orientation="Horizontal"> <TextBox x:Name="Frame5_1" Width="40" Height="40" FontSize="30" TextAlignment="Center" Background="LightCyan"/> <TextBox x:Name="Frame5_2" Width="40" Height="40" FontSize="30" TextAlignment="Center" Background="LightCyan"/> </StackPanel> <TextBlock Text="本轮得分" Background="LightPink"/> <TextBlock x:Name="Frame5_Score" Width="80" Height="40" FontSize="30" TextAlignment="Center" Background="LightGreen" /> </StackPanel> </GroupItem> </GroupBox> <GroupBox Header="第6轮" > <GroupItem> <StackPanel> <StackPanel Orientation="Horizontal"> <TextBox x:Name="Frame6_1" Width="40" Height="40" FontSize="30" TextAlignment="Center" Background="LightCyan"/> <TextBox x:Name="Frame6_2" Width="40" Height="40" FontSize="30" TextAlignment="Center" Background="LightCyan"/> </StackPanel> <TextBlock Text="本轮得分" Background="LightPink"/> <TextBlock x:Name="Frame6_Score" Width="80" Height="40" FontSize="30" TextAlignment="Center" Background="LightGreen" /> </StackPanel> </GroupItem> </GroupBox> <GroupBox Header="第7轮" > <GroupItem> <StackPanel> <StackPanel Orientation="Horizontal"> <TextBox x:Name="Frame7_1" Width="40" Height="40" FontSize="30" TextAlignment="Center" Background="LightCyan"/> <TextBox x:Name="Frame7_2" Width="40" Height="40" FontSize="30" TextAlignment="Center" Background="LightCyan"/> </StackPanel> <TextBlock Text="本轮得分" Background="LightPink"/> <TextBlock x:Name="Frame7_Score" Width="80" Height="40" FontSize="30" TextAlignment="Center" Background="LightGreen" /> </StackPanel> </GroupItem> </GroupBox> <GroupBox Header="第8轮" > <GroupItem> <StackPanel> <StackPanel Orientation="Horizontal"> <TextBox x:Name="Frame8_1" Width="40" Height="40" FontSize="30" TextAlignment="Center" Background="LightCyan"/> <TextBox x:Name="Frame8_2" Width="40" Height="40" FontSize="30" TextAlignment="Center" Background="LightCyan"/> </StackPanel> <TextBlock Text="本轮得分" Background="LightPink"/> <TextBlock x:Name="Frame8_Score" Width="80" Height="40" FontSize="30" TextAlignment="Center" Background="LightGreen" /> </StackPanel> </GroupItem> </GroupBox> <GroupBox Header="第9轮" > <GroupItem> <StackPanel> <StackPanel Orientation="Horizontal"> <TextBox x:Name="Frame9_1" Width="40" Height="40" FontSize="30" TextAlignment="Center" Background="LightCyan"/> <TextBox x:Name="Frame9_2" Width="40" Height="40" FontSize="30" TextAlignment="Center" Background="LightCyan"/> </StackPanel> <TextBlock Text="本轮得分" Background="LightPink"/> <TextBlock x:Name="Frame9_Score" Width="80" Height="40" FontSize="30" TextAlignment="Center" Background="LightGreen" /> </StackPanel> </GroupItem> </GroupBox> <GroupBox Header="第10轮" > <GroupItem> <StackPanel> <StackPanel Orientation="Horizontal"> <TextBox x:Name="Frame10_1" Width="40" Height="40" FontSize="30" TextAlignment="Center" Background="LightCyan"/> <TextBox x:Name="Frame10_2" Width="40" Height="40" FontSize="30" TextAlignment="Center" Background="LightCyan"/> <TextBox x:Name="Frame10_3" Width="40" Height="40" FontSize="30" TextAlignment="Center" Background="LightCyan"/> </StackPanel> <TextBlock Text="本轮得分" Background="LightPink"/> <TextBlock x:Name="Frame10_Score" Width="120" Height="40" FontSize="30" TextAlignment="Center" Background="LightGreen" /> </StackPanel> </GroupItem> </GroupBox> </StackPanel> <TextBlock Text="0-9,X-全中,/-补中" FontSize="16" Foreground="Cyan" Background="DarkKhaki"/> <TextBlock Text="总得分"/> <TextBlock x:Name="SumOfScore" FontSize="80" Text="Your Score" Foreground="Red" Background="Azure"/> <Button Content="计算得分" FontSize="16" Height="40" Width="400" Click="OnClick"/> </StackPanel> </Window>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace BowlingKate { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } public Frame frame1, frame2, frame3, frame4, frame5, frame6, frame7, frame8, frame9, frame10; public void InitFrame() { frame1 = new Frame() { Throw1 = Frame1_1.Text, Throw2 = Frame1_2.Text }; frame2 = new Frame() { Throw1 = Frame2_1.Text, Throw2 = Frame2_2.Text }; frame3 = new Frame() { Throw1 = Frame3_1.Text, Throw2 = Frame3_2.Text }; frame4 = new Frame() { Throw1 = Frame4_1.Text, Throw2 = Frame4_2.Text }; frame5 = new Frame() { Throw1 = Frame5_1.Text, Throw2 = Frame5_2.Text }; frame6 = new Frame() { Throw1 = Frame6_1.Text, Throw2 = Frame6_2.Text }; frame7 = new Frame() { Throw1 = Frame7_1.Text, Throw2 = Frame7_2.Text }; frame8 = new Frame() { Throw1 = Frame8_1.Text, Throw2 = Frame8_2.Text }; frame9 = new Frame() { Throw1 = Frame9_1.Text, Throw2 = Frame9_2.Text }; frame10 = new Frame() { Throw1 = Frame10_1.Text, Throw2 = Frame10_2.Text, Throw3 = Frame10_3.Text }; } public void ShowEachFrameScore(BowlingCalculator bowlingCalculator) { Frame1_Score.Text = bowlingCalculator.CalculatorFrame1(frame1, frame2, frame3).ToString(); Frame2_Score.Text = bowlingCalculator.CalculatorFrame2(frame2, frame3, frame4).ToString(); Frame3_Score.Text = bowlingCalculator.CalculatorFrame3(frame3, frame4, frame5).ToString(); Frame4_Score.Text = bowlingCalculator.CalculatorFrame4(frame4, frame5, frame6).ToString(); Frame5_Score.Text = bowlingCalculator.CalculatorFrame5(frame5, frame6, frame7).ToString(); Frame6_Score.Text = bowlingCalculator.CalculatorFrame6(frame6, frame7, frame8).ToString(); Frame7_Score.Text = bowlingCalculator.CalculatorFrame7(frame7, frame8, frame9).ToString(); Frame8_Score.Text = bowlingCalculator.CalculatorFrame8(frame8, frame9, frame10).ToString(); Frame9_Score.Text = bowlingCalculator.CalculatorFrame9(frame9, frame10).ToString(); Frame10_Score.Text = bowlingCalculator.CalculatorFrame10(frame10).ToString(); } public int CalculatorSumOfScore(BowlingCalculator bowlingCalculator) { int sumOfScore = bowlingCalculator.CalculatorFrame1(frame1, frame2, frame3) + bowlingCalculator.CalculatorFrame2(frame2, frame3, frame4) + bowlingCalculator.CalculatorFrame3(frame3, frame4, frame5) + bowlingCalculator.CalculatorFrame4(frame4, frame5, frame6) + bowlingCalculator.CalculatorFrame5(frame5, frame6, frame7) + bowlingCalculator.CalculatorFrame6(frame6, frame7, frame8) + bowlingCalculator.CalculatorFrame7(frame7, frame8, frame9) + bowlingCalculator.CalculatorFrame8(frame8, frame9, frame10) + bowlingCalculator.CalculatorFrame9(frame9, frame10) + bowlingCalculator.CalculatorFrame10(frame10); return sumOfScore; } private void OnClick(object sender, RoutedEventArgs e) { InitFrame(); ShowEachFrameScore(new BowlingCalculator()); SumOfScore.Text = CalculatorSumOfScore(new BowlingCalculator()).ToString(); } } }
Frame封装成Frame.cs类,代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace BowlingKate { public class Frame { public string Throw1 { get; set; } public string Throw2 { get; set; } public string Throw3 { get; set; } } }
计分类BowlingCalculator.cs,如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace BowlingKate { public class BowlingCalculator { #region CalculatorFrame 1-8 public int CalculatorFrame1(Frame frame1, Frame frame2, Frame frame3) { if (frame1.Throw1 == "X") { if (frame2.Throw1 == "X") { if (frame3.Throw1 == "X") { return 30; } return 20 + Convert.ToInt32(frame3.Throw1); } if (frame2.Throw2 == "/") { return 20; } return 10 + Convert.ToInt32(frame2.Throw1) + Convert.ToInt32(frame2.Throw2); } else if (frame1.Throw2 == "/") { if (frame2.Throw1 == "X") { return 20; } else { return 10 + Convert.ToInt32(frame2.Throw1); } } else { return Convert.ToInt32(frame1.Throw1) + Convert.ToInt32(frame1.Throw2); } } public int CalculatorFrame2(Frame frame2, Frame frame3, Frame frame4) { if (frame2.Throw1 == "X") { if (frame3.Throw1 == "X") { if (frame4.Throw1 == "X") { return 30; } return 20 + Convert.ToInt32(frame4.Throw1); } if (frame3.Throw2 == "/") { return 20; } return 10 + Convert.ToInt32(frame3.Throw1) + Convert.ToInt32(frame3.Throw2); } else if (frame2.Throw2 == "/") { if (frame3.Throw1 == "X") { return 20; } else { return 10 + Convert.ToInt32(frame3.Throw1); } } else { return Convert.ToInt32(frame2.Throw1) + Convert.ToInt32(frame2.Throw2); } } public int CalculatorFrame3(Frame frame3, Frame frame4, Frame frame5) { if (frame3.Throw1 == "X") { if (frame4.Throw1 == "X") { if (frame5.Throw1 == "X") { return 30; } return 20 + Convert.ToInt32(frame5.Throw1); } if (frame4.Throw2 == "/") { return 20; } return 10 + Convert.ToInt32(frame4.Throw1) + Convert.ToInt32(frame4.Throw2); } else if (frame3.Throw2 == "/") { if (frame4.Throw1 == "X") { return 20; } else { return 10 + Convert.ToInt32(frame4.Throw1); } } else { return Convert.ToInt32(frame3.Throw1) + Convert.ToInt32(frame3.Throw2); } } public int CalculatorFrame4(Frame frame4, Frame frame5, Frame frame6) { if (frame4.Throw1 == "X") { if (frame5.Throw1 == "X") { if (frame6.Throw1 == "X") { return 30; } return 20 + Convert.ToInt32(frame6.Throw1); } if (frame5.Throw2 == "/") { return 20; } return 10 + Convert.ToInt32(frame5.Throw1) + Convert.ToInt32(frame5.Throw2); } else if (frame4.Throw2 == "/") { if (frame5.Throw1 == "X") { return 20; } else { return 10 + Convert.ToInt32(frame5.Throw1); } } else { return Convert.ToInt32(frame4.Throw1) + Convert.ToInt32(frame4.Throw2); } } public int CalculatorFrame5(Frame frame5, Frame frame6, Frame frame7) { if (frame5.Throw1 == "X") { if (frame6.Throw1 == "X") { if (frame7.Throw1 == "X") { return 30; } return 20 + Convert.ToInt32(frame7.Throw1); } if (frame6.Throw2 == "/") { return 20; } return 10 + Convert.ToInt32(frame6.Throw1) + Convert.ToInt32(frame6.Throw2); } else if (frame5.Throw2 == "/") { if (frame6.Throw1 == "X") { return 20; } else { return 10 + Convert.ToInt32(frame6.Throw1); } } else { return Convert.ToInt32(frame5.Throw1) + Convert.ToInt32(frame5.Throw2); } } public int CalculatorFrame6(Frame frame6, Frame frame7, Frame frame8) { if (frame6.Throw1 == "X") { if (frame7.Throw1 == "X") { if (frame8.Throw1 == "X") { return 30; } return 20 + Convert.ToInt32(frame8.Throw1); } if (frame7.Throw2 == "/") { return 20; } return 10 + Convert.ToInt32(frame7.Throw1) + Convert.ToInt32(frame7.Throw2); } else if (frame6.Throw2 == "/") { if (frame7.Throw1 == "X") { return 20; } else { return 10 + Convert.ToInt32(frame7.Throw1); } } else { return Convert.ToInt32(frame6.Throw1) + Convert.ToInt32(frame6.Throw2); } } public int CalculatorFrame7(Frame frame7, Frame frame8, Frame frame9) { if (frame7.Throw1 == "X") { if (frame8.Throw1 == "X") { if (frame9.Throw1 == "X") { return 30; } return 20 + Convert.ToInt32(frame9.Throw1); } if (frame8.Throw2 == "/") { return 20; } return 10 + Convert.ToInt32(frame8.Throw1) + Convert.ToInt32(frame8.Throw2); } else if (frame7.Throw2 == "/") { if (frame8.Throw1 == "X") { return 20; } else { return 10 + Convert.ToInt32(frame8.Throw1); } } else { return Convert.ToInt32(frame7.Throw1) + Convert.ToInt32(frame7.Throw2); } } public int CalculatorFrame8(Frame frame8, Frame frame9, Frame frame10) { if (frame8.Throw1 == "X") { if (frame9.Throw1 == "X") { if (frame10.Throw1 == "X") { return 30; } return 20 + Convert.ToInt32(frame10.Throw1); } if (frame9.Throw2 == "/") { return 20; } return 10 + Convert.ToInt32(frame9.Throw1) + Convert.ToInt32(frame9.Throw2); } else if (frame8.Throw2 == "/") { if (frame9.Throw1 == "X") { return 20; } else { return 10 + Convert.ToInt32(frame9.Throw1); } } else { return Convert.ToInt32(frame8.Throw1) + Convert.ToInt32(frame8.Throw2); } } #endregion public int CalculatorFrame9(Frame frame9, Frame frame10) { if (frame9.Throw1 == "X") { if (frame10.Throw1 == "X") { if (frame10.Throw2 == "X") { return 30; } else { return 20 + Convert.ToInt32(frame10.Throw2); } } else if (frame10.Throw2 == "/") { return 20; } else { return 10 + Convert.ToInt32(frame10.Throw1) + Convert.ToInt32(frame10.Throw2); } } else if (frame9.Throw2 == "/") { if (frame10.Throw1 == "X") { return 20; } else { return 10 + Convert.ToInt32(frame10.Throw1); } } else { return Convert.ToInt32(frame9.Throw1) + Convert.ToInt32(frame9.Throw2); } } public int CalculatorFrame10(Frame frame10) { if (frame10.Throw1 == "X") { if (frame10.Throw2 == "X") { if (frame10.Throw3 == "X") { return 30; } else { return 20 + Convert.ToInt32(frame10.Throw3); } } else if (frame10.Throw3 == "/") { return 20; } else { return 10 + Convert.ToInt32(frame10.Throw2) + Convert.ToInt32(frame10.Throw3); } } else if (frame10.Throw2 == "/") { if (frame10.Throw3 == "X") { return 20; } else { return 10 + Convert.ToInt32(frame10.Throw3); } } else { return Convert.ToInt32(frame10.Throw1) + Convert.ToInt32(frame10.Throw2); } } } }
程序运行如下:
单元测试UnitTest1.cs如下:
using System; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace UnitTestBowlingCalculator { [TestClass] public class UnitTest1 { public static BowlingKate.Frame frame1, frame2, frame3, frame4, frame5, frame6, frame7, frame8, frame9, frame10; [ClassInitialize] public static void InitializeFrames(TestContext context) { frame1 = new BowlingKate.Frame() { Throw1 = "X" }; frame2 = new BowlingKate.Frame() { Throw1 = "3", Throw2 = "/" }; frame3 = new BowlingKate.Frame() { Throw1 = "X" }; frame4 = new BowlingKate.Frame() { Throw1 = "X" }; frame5 = new BowlingKate.Frame() { Throw1 = "X" }; frame6 = new BowlingKate.Frame() { Throw1 = "X" }; frame7 = new BowlingKate.Frame() { Throw1 = "X" }; frame8 = new BowlingKate.Frame() { Throw1 = "X" }; frame9 = new BowlingKate.Frame() { Throw1 = "X" }; frame10 = new BowlingKate.Frame() { Throw1 = "X", Throw2 = "2", Throw3 = "/" }; } [TestMethod] public void TestCalculatorFrame1() { BowlingKate.BowlingCalculator bowlingCalculator = new BowlingKate.BowlingCalculator(); int score1=bowlingCalculator.CalculatorFrame1(frame1, frame2, frame3); Assert.AreEqual(score1, 20); } [TestMethod] public void TestCalculatorFrame2() { BowlingKate.BowlingCalculator bowlingCalculator = new BowlingKate.BowlingCalculator(); int score1 = bowlingCalculator.CalculatorFrame2(frame2, frame3, frame4); Assert.AreEqual(score1, 20); } [TestMethod] public void TestCalculatorFrame3() { BowlingKate.BowlingCalculator bowlingCalculator = new BowlingKate.BowlingCalculator(); int score1 = bowlingCalculator.CalculatorFrame3(frame3, frame4, frame5); Assert.AreEqual(score1, 30); } [TestMethod] public void TestCalculatorFrame9() { BowlingKate.BowlingCalculator bowlingCalculator = new BowlingKate.BowlingCalculator(); int score1 = bowlingCalculator.CalculatorFrame9(frame9, frame10); Assert.AreEqual(score1, 22); } [TestMethod] public void TestCalculatorFrame10() { BowlingKate.BowlingCalculator bowlingCalculator = new BowlingKate.BowlingCalculator(); int score1 = bowlingCalculator.CalculatorFrame10(frame10); Assert.AreEqual(score1, 20); } } }
测试运行如下:
说明:计分算法1-8相同,应重构。
希望对正好遇到Bowling计分的博友有点帮助~
参考后续博文:MoQ