赛狗日源码分享 《Head First C#》第三版

@

前言

这个赛狗日,是给个框架让自己写。感觉难度有点大,语言和控件不熟。读他的说明和程序结构读了两三遍。最后才明白要干啥。写是写出来了,但是我感觉还可以优化,优化在最后说明,线来看下效果

效果展示

效果展示

步骤

熟读任务说明

写一个程序第一步就是要明白,它要干啥,他怎么实现。而不是一遍写一遍想,写写改改,这样麻烦不说而且还很捞,我们这种新手一般都没有哪个本事一写就过。这本书很好,给了程序的结构和逻辑,只要自己想方法实现 就行了。所以要先读清楚任务说明,以免越写越乱。

搭界面

书上给的网站应该换地址了,进去不是的,是他们公司官网。无奈网上找不到合适的图片。只有自己画了,用PPT简单画一个。
👇👇👇👇自己画的跑道
然后就按照书上的界面搭建一个。👇👇👇👇
搭建的界面
图片网上好找。拍好布局,设置好控件的属性就OK,不想调图片尺寸,我就用的 背景图片填充,然后背景图片设为自适应。

开始代码

按照书上的结构代码分为一下四个部分:

  1. Form:界面控件的逻辑控制。
  2. Greyhound:赛狗的对象,每个对象控制一条狗。
  3. Guy:赌徒对象,一个赌徒有一个对应的一个对象。
  4. Bet:赌局对象(个人理解),控制一个赌徒本局的下注情况。

From.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Day_at_the_Races
{
    public partial class Form1 : Form
    {
        Greyhound[] greyhounds = new Greyhound[4];//声明一个数组管理Grehound对象
        Guy[] guys = new Guy[3];//声明一个数组管理Guy对象
        Random random = new Random();//创建一个Random对象。

        public Form1()
        {
            InitializeComponent();

            initDog();//初始化狗
            initGuy();//初始化赌徒和对应的赌注。

        }


        /// <summary>
        /// 初始化比赛的狗:绑定狗的图片对象,赛道的长度,起点位置,随机Random对象;同时,初始化狗的位置。
        /// </summary>

        public void initDog()
        {
            //创建对象,同时对属性赋值。
            greyhounds[0] = new Greyhound()
            {
                MypictureBox = dog1,
                StartingPosition = raceRoad.Left,
                RacetrackLength = raceRoad.Width,
                randomizer = random
            };
            //重置狗的位置
            greyhounds[0].TakeStaringPosition();
            //Console.WriteLine("0号狗位置"+ greyhounds[0].StartingPosition);
            //Console.WriteLine("0号狗图片左边位置" + greyhounds[0].MypictureBox.Left);
            greyhounds[1] = new Greyhound()
            {
                MypictureBox = dog2,
                StartingPosition = raceRoad.Left,
                RacetrackLength = raceRoad.Width,
                randomizer = random
            };
            greyhounds[1].TakeStaringPosition();
            //0Console.WriteLine("1号狗位置" + greyhounds[1].StartingPosition);
            //Console.WriteLine("1号狗图片左边位置" + greyhounds[1].MypictureBox.Left);
            greyhounds[2] = new Greyhound()
            {
                MypictureBox = dog3,
                StartingPosition = raceRoad.Left,
                RacetrackLength = raceRoad.Width,
                randomizer = random
            };
            greyhounds[2].TakeStaringPosition();
            //Console.WriteLine("2号狗位置" + greyhounds[2].StartingPosition);
            //Console.WriteLine("2号狗图片左边位置" + greyhounds[2].MypictureBox.Left);
            greyhounds[3] = new Greyhound()
            {
                MypictureBox = dog4,
                StartingPosition = raceRoad.Left,
                RacetrackLength = raceRoad.Width,
                randomizer = random
            };
            greyhounds[3].TakeStaringPosition();
            //Console.WriteLine("3号狗位置" + greyhounds[3].StartingPosition);
            //Console.WriteLine("3号狗图片左边位置" + greyhounds[3].MypictureBox.Left);
        }

        /// <summary>
        /// 初始化赌徒,姓名 ,赌资,radio button,和label;最后创建每个的赌注(bet)对象;
        /// </summary>
        public void initGuy()
        {
            guys[0] = new Guy()
            {
                Name = "卢本伟",
                Cash = 50,
                MyRadio = radiolbw,
                Mylabel = lablbwInfo
            };

            guys[1] = new Guy()
            {
                Name = "孙笑川",
                Cash = 60,
                MyRadio = radsxc,
                Mylabel = labsxcInfo
            };


            guys[2] = new Guy()
            {
                Name = "大司马",
                Cash = 20,
                MyRadio = raddsm,
                Mylabel = labdsmInfo
            };

            initbet();
        }

        /// <summary>
        /// 初始化bet对象,默认以0未初始化。
        /// </summary>
        public void initbet()
        {
            guys[0].UpdateLabel();
            guys[0].PlaceBet(0, 0);

            guys[1].UpdateLabel();
            guys[1].PlaceBet(0, 0);

            guys[2].UpdateLabel();
            guys[2].PlaceBet(0, 0);
        }

        /// <summary>
        /// timer调用的方法,每次调用都进行四条狗的Run函数,检测到有true返回就开始进行Guy的Collect(),结算。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void timer1_Tick(object sender, EventArgs e)
        {
            for (int i = 0; i < 4; i++)
            {

                if (greyhounds[i].Run())
                {
                    timer1.Stop();
                    for (int j = 0; j < 3; j++)
                    {
                        guys[j].Collect(i);

                    }
                    //显示提示信息
                    MessageBox.Show("本局" + i + "号狗取得胜利!");
                    //释放每个狗的对象
                    for (int k = 0; k < 4; k++)
                    {
                        greyhounds[k] = null;
                    }
                    //初始化狗
                    initDog();
                    //初始化赌注,赌注对象已经在Collect()里面删除。
                    initbet();
                    //将面板组件激活,进行下次赌局。
                    groupBox1.Enabled = true;
                    //重置单选框
                    radiolbw.Checked = false;
                    radsxc.Checked = false;
                    raddsm.Checked = false;
                    money.Text = "0";//下注输入框重置
                    dog.Text = "0";
                    break;//一定要加上,结束循环,如果不加初始化后还会调用greyhounds[i].Run(),就会不在原点。
                }

            }
        }

        /// <summary>
        /// 点击后进行最下方Label跟新和两个输入框的刷新。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void radiolbw_Click(object sender, EventArgs e)
        {

            labguy.Text = "卢本伟压注";
            money.Text = "0";
            dog.Text = "0";
        }

        private void raddsm_Click(object sender, EventArgs e)
        {
            labguy.Text = "大司马压注";
            money.Text = "0";
            dog.Text = "0";
        }

        private void radsxc_Click(object sender, EventArgs e)
        {
            labguy.Text = "孙笑川压注";
            money.Text = "0";
            dog.Text = "0";
        }

        /// <summary>
        /// 比赛开始按钮,激活计时器。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_start_Click(object sender, EventArgs e)
        {

            //验证本局是否有人下注
            if (guys[0].Mybet.Amount == 0 && guys[1].Mybet.Amount == 0 && guys[2].Mybet.Amount == 0)
            {
                MessageBox.Show("暂无选手下注!");
            }
            else
            {
                groupBox1.Enabled = false;//禁用下注面板
                timer1.Interval = 100;//设置计时器的时间间隔为100ms。
                timer1.Start();//开始计时器
            }
        }

        /// <summary>
        /// 确认下注按钮,跟新Guy的bet对象,跟新label
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_ok_Click(object sender, EventArgs e)
        {
            //如果此时选中的是卢本伟的单选按钮。
            if (radiolbw.Checked)
            {
                if (Int32.Parse(money.Text) < 5)
                {
                    MessageBox.Show("下注金额至少为5元!");
                }
                else if (Int32.Parse(dog.Text) < 0 || Int32.Parse(dog.Text) > 3)
                {
                    MessageBox.Show("没有" + Int32.Parse(dog.Text) + "号狗!");
                }
                else
                {
                    guys[0].PlaceBet(Int32.Parse(money.Text), Int32.Parse(dog.Text));//更新bet对象
                    guys[0].UpdateLabel();//更新label
                }

            }
            //如果此时选中的是孙笑川的单选按钮。
            if (radsxc.Checked)
            {
                if (Int32.Parse(money.Text) < 5)
                {
                    MessageBox.Show("下注金额至少为5元!");
                }
                else if (Int32.Parse(dog.Text) < 0 || Int32.Parse(dog.Text) > 3)
                {
                    MessageBox.Show("没有" + Int32.Parse(dog.Text) + "号狗!");
                }
                else
                {
                    guys[1].PlaceBet(Int32.Parse(money.Text), Int32.Parse(dog.Text));
                    guys[1].UpdateLabel();
                }

            }
            //如果此时选中的是大司马的单选按钮。
            if (raddsm.Checked)
            {
                if (Int32.Parse(money.Text) < 5)
                {
                    MessageBox.Show("下注金额至少为5元!");
                }
                else if (Int32.Parse(dog.Text) < 0 || Int32.Parse(dog.Text) > 3)
                {
                    MessageBox.Show("没有" + Int32.Parse(dog.Text) + "号狗!");
                }
                else
                {
                    guys[2].PlaceBet(Int32.Parse(money.Text), Int32.Parse(dog.Text));
                    guys[2].UpdateLabel();
                }

            }
        }
    }
}

Greyhound.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Day_at_the_Races
{
    public class Greyhound
    {
        public int StartingPosition;//起点位置,我设置的是以赛道的左边为起点
        public int RacetrackLength;//赛道长度,就是赛道图片的with宽度,由于我算终点的宽度,我量的是100个像素,所以计算的时候要减去95(营造冲线假象)
        public PictureBox MypictureBox = null;//狗的图片
        public int Location = 0;//狗跑出的距离,再次距离上进行位置叠加
        public Random randomizer;

        public bool Run()
        {
            bool isWin = false;//输赢的标志变量

            Location += randomizer.Next(0,20);//随机对象,随机移动0-20个像素。

            MypictureBox.Left = StartingPosition + Location;//位置更新
            if(MypictureBox.Left>= (RacetrackLength-95) - MypictureBox.Width)//判断是否冲线,95在上方属性有解释。
            {
                isWin = true;
            }
            
            return isWin;
        }

        //初始狗的位置
        public void TakeStaringPosition()
        {
            this.MypictureBox.Left = StartingPosition;
            this.Location = 0;
        }
        
    }
}

Guy.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Day_at_the_Races
{
    public class Guy
    {
        public string Name;//姓名
        public Bet Mybet;//赌注对象
        public int Cash;//赌资

        public RadioButton MyRadio;//radio button对象
        public Label Mylabel;//label对象

        //跟新radio button对象
        public void UpdateLabel()
        {
            MyRadio.Text = Name + "有" + Cash + "元";

        }

        //清除赌局对象
        public void ClearBet()
        {
            Mybet = null;
        }

        //创建赌局对象
        public bool PlaceBet(int BetAmount,int DogTOWin)
        {
            if (Mybet != null)//为空就初始化
            {
                //if (BetAmount < 5)
                //{
                //    MessageBox.Show("下注金额至少为5元!");  //已经在开始按钮处判断
                //    return false;
                //}
                if(BetAmount > Cash){
                    MessageBox.Show(Name+"的金额不足"+BetAmount+"!");
                    
                    return false;
                }
                else
                {
                    //进行行赌局对象属性的更新
                    Console.WriteLine("下注成功!");
                    Mybet.Amount = BetAmount;
                    Mybet.dog = DogTOWin;
                    Mylabel.Text = Mybet.GetDiscreaption();
                    return true;
                }
                
            }
            else
            {
                //初始化
                Mybet = new Bet() { Amount = BetAmount, Better = this, dog = DogTOWin };
                Mylabel.Text = Mybet.GetDiscreaption();
                return false;
            }
            
        }

        //进行赌局结算,更新资产,radio button的文字,删除上局的bet对象。
        public void Collect(int Winner)
        {
            Cash += Mybet.Payout(Winner);
            ClearBet();
            MyRadio.Text= Name + "有" + Cash + "元";
        }
        
    }
}

Bet.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Day_at_the_Races
{
    public class Bet
    {
        public int Amount;//下注金额
        public int dog;//压得狗
        public Guy Better;//赌徒

        //赌徒在本局一个描述
        public string GetDiscreaption()
        {
            string dis = "";
            if (Amount == 0)
            {
                dis = Better.Name + "没有下注";
            }
            else
            {
                
                dis = Better.Name + "下注" + Amount + "元,赌" + dog + "号狗会赢";
                
            }
            return dis;

        }

        //返回本局的输赢金额
        public int Payout(int Winner)
        {
            if (dog == Winner)
            {
                return Amount;
            }
            else
            {
                return -Amount;
            }

        }
    }
}

写在后面的话

本次遇见的一些问题

论坛给了这么好的交流平台,当然要交流下自己的问题。作为初学者更应该交流。

  1. 书要仔细,最开始遗漏了一个bet按钮,书上也不显眼,纠结了好久Raadiobutton功能写不好,总不满意。最后一看下注那句话里面藏个按钮。。。这样就好办了。
  2. 最后判断完胜利者,重新初始化狗的位置时总有偏移。👇👇
    在这里插入图片描述
    然后我输出图片的位置查看一下初始化时图片的位置发现没毛病。👇
    在这里插入图片描述
    然后我以为是电脑有问题了,直到一次0号狗跑了第一和一次3号狗跑了第一,一对比才想到,没有控制好结束的逻辑,结束后不适用break初始化后还会调用后面狗的Run(),从而发生偏移。
    在这里插入图片描述

关于优化

可以设定狗的特性

可以参照游戏设定狗的一些特性,让比赛随机,但又可以预料。

去掉Bet类

我觉得哪个Bet类显得有点碍事,让程序有点复杂。可以直接合并到Guy类里面。这个只是个人见解,如不正确还望大神指点

自己的不足

我写完后自己有看了遍代码,虽然完成了书上的功能,但是觉得自己也有写偏的。比如:

  • Bet类默认都创建,我觉得书上得意思是,这局赌了才会创建一个Bet对象,而我是默认都创建了。同时还增加了多余的判断条件。
  • 关于label的跟新我都是依赖Bet类里面的GetDiscreaption()来获得文本,总体我觉得不好。

可能还有许多不足的地方,希望有大佬看出来能够交流下,我以后避免再犯这样的错误。❤❤❤🙇‍♂️

在这里插入图片描述
2020-2-24

posted @ 2020-02-24 21:02  Boting_是我  阅读(756)  评论(0编辑  收藏  举报