C/C++新手学习项目(三) 魔兽世界之三:开战

回顾

本题是北大程序设计MOOC的课程大作业。老实说,如果当作一个小项目来看待的话,这道题的代码量不是很大,不过难得的是,这道题不涉及任何C++以外的操作系统、计算机网络、各种框架的使用等知识。因此,在正式学习优秀的开源项目之前,用这道题来热热身,找一找撸C++代码的感觉,还是很合适的。

首先反思的一点是关于类的设计,这个可以说是OOP编程的重点。要做好一个项目,一定要在前期花大量的时间来设计类,定义接口,这样会给自己省去很多麻烦。这次就是因为前期没有好好思考清楚,甚至没认真读完整个题目就开始凭感觉去写,前面给自己挖坑,后面再去修改,花了更多的精力,而且代码中也存在着可以优化的地方。所幸代码量不算很大,大概用了一天时间来coding,然后又摸鱼半天来debug。

另外,对于这种偏工程性的题目,而不是leetcode那种百行左右基本就可以搞定的算法题来说,最好先看一下测试用例或者边写边测试。写完一个功能测试一下,可以给最后的debug剩很多时间。

这道题的输出比较多,可以使用一些Linux命令行下的文件比较工具,比如diff。当然也可以使用一些在线的文本比较网站,会更容易操作一些。

补充

这两天在学习java,java是一种比C++更纯粹的面向对象语言。很多人学习的时候,都不注意一些例如:命名规范、注释这样的细节(比如我下面的代码Orz),觉得代码能跑就行,其实开发中是要养成这些好习惯的,这样能提高代码阅读性,方便自己,方便别人。

命名规范

类型 要求
包名 多单词组成时所有字母都小写 ,如xxxyyyzzz
类名、接口名 多单词组成时,所有单词的首字母大写 ,如XxxYyyZzz
变量名、方法名 第一个单词首字母小写,后面单词首字母大写,如xxxYyyZzz
常量名 所有字母都大写。多单词时每个单词用下划线连接 XXX_YYY_ZZZ

题目描述

加粗的部分是一些细节,需要注意,按照题中所给的要求输出。
魔兽世界的西面是红魔军的司令部,东面是蓝魔军的司令部。两个司令部之间是依次排列的若干城市,城市从西向东依次编号为1,2,3 … N ( N <= 20)。红魔军的司令部算作编号为0的城市,蓝魔军的司令部算作编号为N+1的城市。司令部有生命元,用于制造武士。

两军的司令部都会制造武士。武士一共有dragon 、ninja、iceman、lion、wolf 五种。每种武士都有编号、生命值、攻击力这三种属性。

双方的武士编号都是从1开始计算。红方制造出来的第n 个武士,编号就是n。同样,蓝方制造出来的第n 个武士,编号也是n。

武士在刚降生的时候有一个初始的生命值,生命值在战斗中会发生变化,如果生命值减少到0(生命值变为负数时应当做变为0处理),则武士死亡(消失)。

武士可以拥有武器。武器有三种,sword, bomb,和arrow,编号分别为0,1,2。

sword的攻击力是使用者当前攻击力的20%(去尾取整)。

bomb的攻击力是使用者当前攻击力的40%(去尾取整),但是也会导致使用者受到攻击,对使用者的攻击力是对敌人取整后的攻击力的1/2(去尾取整)。Bomb一旦使用就没了。

arrow的攻击力是使用者当前攻击力的30%(去尾取整)。一个arrow用两次就没了。

武士降生后就朝对方司令部走,在经过的城市如果遇到敌人(同一时刻每个城市最多只可能有1个蓝武士和一个红武士),就会发生战斗。战斗的规则是:

在奇数编号城市,红武士先发起攻击

在偶数编号城市,蓝武士先发起攻击

战斗开始前,双方先对自己的武器排好使用顺序,然后再一件一件地按顺序使用。编号小的武器,排在前面。若有多支arrow,用过的排在前面。排好序后,攻击者按此排序依次对敌人一件一件地使用武器。如果一种武器有多件,那就都要用上。每使用一件武器,被攻击者生命值要减去武器攻击力。如果任何一方生命值减为0或小于0即为死去。有一方死去,则战斗结束。

双方轮流使用武器,甲用过一件,就轮到乙用。某一方把自己所有的武器都用过一轮后,就从头开始再用一轮。如果某一方没有武器了,那就挨打直到死去或敌人武器用完。武器排序只在战斗前进行,战斗中不会重新排序。

如果双方武器都用完且都还活着,则战斗以平局结束。如果双方都死了,也算平局。

有可能由于武士自身攻击力太低,而导致武器攻击力为0。攻击力为0的武器也要使用。如果战斗中双方的生命值和武器的状态都不再发生变化,则战斗结束,算平局。

战斗的胜方获得对方手里的武器。武士手里武器总数不超过10件。缴获武器时,按照武器种类编号从小到大缴获。如果有多件arrow,优先缴获没用过的。

如果战斗开始前双方都没有武器,则战斗视为平局。如果先攻击方没有武器,则由后攻击方攻击。

不同的武士有不同的特点。

编号为n的dragon降生时即获得编号为n%3 的武器。dragon在战斗结束后,如果还没有战死,就会欢呼。

编号为n的ninjia降生时即获得编号为n%3 和(n+1)%3的武器。ninja 使用bomb不会让自己受伤。

编号为n的iceman降生时即获得编号为n%3 的武器。iceman每前进一步,生命值减少10%(减少的量要去尾取整)。

编号为n的lion降生时即获得编号为n%3 的武器。lion 有“忠诚度”这个属性,其初始值等于它降生之后其司令部剩余生命元的数目。每前进一步忠诚度就降低K。忠诚度降至0或0以下,则该lion逃离战场,永远消失。但是已经到达敌人司令部的lion不会逃跑。lion在己方司令部可能逃跑。

wolf降生时没有武器,但是在战斗开始前会抢到敌人编号最小的那种武器。如果敌人有多件这样的武器,则全部抢来。Wolf手里武器也不能超过10件。如果敌人arrow太多没法都抢来,那就先抢没用过的。如果敌人也是wolf,则不抢武器。

以下是不同时间会发生的不同事件:

在每个整点,即每个小时的第0分, 双方的司令部中各有一个武士降生。

红方司令部按照iceman、lion、wolf、ninja、dragon 的顺序制造武士。

蓝方司令部按照lion、dragon、ninja、iceman、wolf 的顺序制造武士。

制造武士需要生命元。

制造一个初始生命值为m 的武士,司令部中的生命元就要减少m 个。

如果司令部中的生命元不足以制造某本该造的武士,那就从此停止制造武士。

在每个小时的第5分,该逃跑的lion就在这一时刻逃跑了。

在每个小时的第10分:所有的武士朝敌人司令部方向前进一步。即从己方司令部走到相邻城市,或从一个城市走到下一个城市。或从和敌军司令部相邻的城市到达敌军司令部。

在每个小时的第35分:在有wolf及其敌人的城市,wolf要抢夺对方的武器。

在每个小时的第40分:在有两个武士的城市,会发生战斗。

在每个小时的第50分,司令部报告它拥有的生命元数量。

在每个小时的第55分,每个武士报告其拥有的武器情况。

武士到达对方司令部后就算完成任务了,从此就呆在那里无所事事。

任何一方的司令部里若是出现了敌人,则认为该司令部已被敌人占领。

任何一方的司令部被敌人占领,则战争结束。战争结束之后就不会发生任何事情了。

给定一个时间,要求你将从0点0分开始到此时间为止的所有事件按顺序输出。事件及其对应的输出样例如下:

  1. 武士降生

输出样例:000:00 blue dragon 1 born

表示在0点0分,编号为1的蓝魔dragon武士降生

如果造出的是lion,那么还要多输出一行,例:

000:00 blue lion 1 born

Its loyalty is 24

表示该lion降生时的忠诚度是24

  1. lion逃跑

输出样例:000:05 blue lion 1 ran away

表示在0点5分,编号为1的蓝魔lion武士逃走

  1. 武士前进到某一城市

输出样例:

000:10 red iceman 1 marched to city 1 with 20 elements and force 30

表示在0点10分,红魔1号武士iceman前进到1号城市,此时他生命值为20,攻击力为30

对于iceman,输出的生命值应该是变化后的数值

  1. wolf抢敌人的武器

000:35 blue wolf 2 took 3 bomb from red dragon 2 in city 4

表示在0点35分,4号城市中,红魔1号武士wolf 抢走蓝魔2号武士dragon 3个bomb。为简单起见,武器不写复数形式

  1. 报告战斗情况

战斗只有3种可能的输出结果:

000:40 red iceman 1 killed blue lion 12 in city 2 remaining 20 elements

表示在0点40分,1号城市中,红魔1号武士iceman 杀死蓝魔12号武士lion后,剩下生命值20

000:40 both red iceman 1 and blue lion 12 died in city 2

注意,把红武士写前面

000:40 both red iceman 1 and blue lion 12 were alive in city 2

注意,把红武士写前面

  1. 武士欢呼

输出样例:003:40 blue dragon 2 yelled in city 4

  1. 武士抵达敌军司令部

输出样例:001:10 red iceman 1 reached blue headquarter with 20 elements and force 30

(此时他生命值为20,攻击力为30)对于iceman,输出的生命值和攻击力应该是变化后的数值

  1. 司令部被占领

输出样例:003:10 blue headquarter was taken

9)司令部报告生命元数量

000:50 100 elements in red headquarter

000:50 120 elements in blue headquarter

表示在0点50分,红方司令部有100个生命元,蓝方有120个

10)武士报告情况

000:55 blue wolf 2 has 2 sword 3 bomb 0 arrow and 7 elements

为简单起见,武器都不写复数形式。elements一律写复数,哪怕只有1个

交代武器情况时,次序依次是:sword,bomb, arrow。

输出事件时:

首先按时间顺序输出;

同一时间发生的事件,按发生地点从西向东依次输出. 武士前进的事件, 算是发生在目的地。

在一次战斗中有可能发生上面的 5 至 6 号事件。这些事件都算同时发生,其时间就是战斗开始时间。一次战斗中的这些事件,序号小的应该先输出。

两个武士同时抵达同一城市,则先输出红武士的前进事件,后输出蓝武士的。

对于同一城市,同一时间发生的事情,先输出红方的,后输出蓝方的。

显然,8号事件发生之前的一瞬间一定发生了7号事件。输出时,这两件事算同一时间发生,但是应先输出7号事件

虽然任何一方的司令部被占领之后,就不会有任何事情发生了。但和司令部被占领同时发生的事件,全都要输出。

输入
第一行是t,代表测试数据组数

每组样例共三行。

第一行,4个整数 M,N,K, T。其含义为:
每个司令部一开始都有M个生命元( 1 <= M <= 100000)
两个司令部之间一共有N个城市( 1 <= N <= 20 )
lion每前进一步,忠诚度就降低K。(0<=K<=100)
要求输出从0时0分开始,到时间T为止(包括T) 的所有事件。T以分钟为单位,0 <= T <= 6000

第二行:五个整数,依次是 dragon 、ninja、iceman、lion、wolf 的初始生命值。它们都大于0小于等于200

第三行:五个整数,依次是 dragon 、ninja、iceman、lion、wolf 的攻击力。它们都大于0小于等于200
输出
对每组数据,先输出一行:

Case n:

如对第一组数据就输出 Case 1:

然后按恰当的顺序和格式输出到时间T为止发生的所有事件。每个事件都以事件发生的时间开头,时间格式是“时: 分”,“时”有三位,“分”有两位。

样例输入

1
20 1 10 400
20 20 30 10 20
5 5 5 5 5

样例输出

Case 1:
000:00 blue lion 1 born
Its loyalty is 10
000:10 blue lion 1 marched to city 1 with 10 elements and force 5
000:50 20 elements in red headquarter
000:50 10 elements in blue headquarter
000:55 blue lion 1 has 0 sword 1 bomb 0 arrow and 10 elements
001:05 blue lion 1 ran away
001:50 20 elements in red headquarter
001:50 10 elements in blue headquarter
002:50 20 elements in red headquarter
002:50 10 elements in blue headquarter
003:50 20 elements in red headquarter
003:50 10 elements in blue headquarter
004:50 20 elements in red headquarter
004:50 10 elements in blue headquarter
005:50 20 elements in red headquarter
005:50 10 elements in blue headquarter

提示
请注意浮点数精度误差问题。OJ上的编译器编译出来的可执行程序,在这方面和你电脑上执行的程序很可能会不一致。5 * 0.3 的结果,有的机器上可能是 15.00000001,去尾取整得到15,有的机器上可能是14.9999999,去尾取整后就变成14。因此,本题不要写 5 * 0.3,要写 5 * 3 / 10。

我的代码

代码在执行效率、类的设计等等方面都还有优化的空间,经测试,可以通过全部测试用例。测试用例为出题老师提供,也附在下面。
代码需要按C11的标准编译,如果使用vscode或者命令行,需要使用如下命令:

g++ -std=c++11 .\filename.cpp -o filename

如果使用DEV等IDE,需要在编译器选项选择中加入-std=c++11,以DEV为例:
DEV编译设置
代码:

#include<iostream>
#include<stdio.h>
#include<string>
#include<string.h>
#include<vector>
#include<algorithm>
#include<math.h>

#define SOLDIER_NUM 5 //武士种类
#define WEAPON_NUM 3 // 武器种类

#define DRAGON 0
#define NINJA 1
#define ICEMAN 2
#define LION 3
#define WOLF 4

#define RED 0
#define BLUE 1

#define SWORD 0
#define BOMB 1
#define ARROW 2

using namespace std;

class Soldier;
class Dragon;
class Ninja;
class Iceman;
class Lion;
class Wolf;


typedef struct{
   
    Soldier* p1;//for red
    Soldier* p2;//for blue
    int counter;
}City;

class MAP{
   
public:
    City City_list[25];
    static int City_NUM;
    MAP(int _City_NUM){
   
        City_NUM = _City_NUM;
        Init();
    }
    void Init(){
   
        for(int i=0;i<25;i++){
   
            City_list[i].p1=NULL;
            City_list[i].p2=NULL;
            City_list[i].counter=0;
        }
    }
    int move(int _time);//武士在地图上移动 返回0 for 结束, 1 for 继续
    void Attack(int hour);//红蓝武士PK
    void Lion_run(int hour);
    void Print_Soldier(int hour);
    void Wolf_rob(int hour);
    void clear();
};

class Headquarter{
   
private:
    int total_soldier;//武士总数
    int color;// 0 for red, 1 for blue
    int total_life;//总生命值
    bool stop_making;
    int curmaking_index;//当前制造的武士索引号
    int soldier_num[SOLDIER_NUM];//各类武士的数量
    Soldier* psoldier;//指向当前创造的武士
    int location;// 0 for red,n+1 for blue
public:
    int making_list[SOLDIER_NUM];//武士制作顺序
    Headquarter(int _color,int _total_life){
   
        total_soldier = 0;
        color = _color;
        total_life = _total_life;
        stop_making = false;
        curmaking_index = 0;
        if(color == RED) location = 0;
        else location = MAP::City_NUM + 1;
        memset(soldier_num,0,sizeof(soldier_num));
    }
    bool Make_soldier(int hour,MAP *pmap);//制造一个武士
    void Print_total_life(int hour){
   
        if(color == RED) printf("%03d:50 %d elements in red headquarter\n",hour,total_life);
        else printf("%03d:50 %d elements in blue headquarter\n",hour,total_life);
    }
    friend class Soldier;//声明友元
    friend class Dragon;
    friend class Ninja;
    friend class Iceman;
    friend class Lion;
    friend class Wolf;
};

class Weapon{
   
public:
    int kind;
    int force;//武器攻击力
    int HP;
    //static int InitForce[WEAPON_NUM];
    static const char* Name_of_weapon[WEAPON_NUM];
    static int HP_of_weapon[WEAPON_NUM];//武器耐久
    Weapon(int _kind):kind(_kind){
   
        HP = Weapon::HP_of_weapon[kind];
    }
};

bool mysort(const Weapon & A,const Weapon & B){
   //弓箭耐久低的排在前面
    if(A.kind != B.kind) return A.kind < B.kind;
    if(A.kind == ARROW){
   
        return A.HP < B.HP;
    }
    return true;
}

bool mysort_high(const Weapon & A,const Weapon &B){
   //耐久高的排前面
    if(A.kind != B.kind) return A.kind < B.kind;
    if(A.kind == ARROW){
   
        return A.HP > B.HP;
    }
    return true;
}

class Soldier{
   
protected:
    Headquarter *pHeadquarter;//表明阵营
    int No;//编号
    int kind;//类型 0 for dragon,1 for ninja, 2 for iceman, 3 for lion, 4 for wolf
    bool alive;
    int location;//位置
    int life;
    int Weapon_num;//拥有的武器数量
    int curWeapon_index;//当前使用的武器序号
    vector<Weapon> Weapon_list;
public:
    static const char* Name_of_soldier[SOLDIER_NUM];//字符串常量最好使用const char*,否则编译器会报warning
    static int Force_of_soldier[SOLDIER_NUM];
    static int HP_of_soldier[SOLDIER_NUM];
    Soldier(Headquarter* p,int _kind){
   
        pHeadquarter = p;
        kind = _kind;
        alive = true;
        location = pHeadquarter->location;
        No = pHeadquarter->total_soldier;
        life = Soldier::HP_of_soldier[kind];
    }
    virtual void Print(int hour);
    virtual void Printwin(Soldier *p, int hour,int i);
    int PK(Soldier *p,int hour,int i);//调用者先攻击 在第i个城市 return 0 for red win,1 for blue  win,2 for both alive, 3 for both died
    bool weapon_Zero();
    void attack(Soldier *p);//攻击一下
    void deleteWeapon();
    void sort_weapon();
    void sort_weapon_high();
    void get_weapon(Soldier *p);
    int get_weapon_force(int kind);
    void Print_weapon(int hour);
    bool isalive();
    int return_kind();
    int count_weapon(int kind);
    virtual int return_Lion_loyalty(){
   }; 
    virtual void yell(int hour,int i){
   };
    virtual void Lion_Incloyalty(){
   };
    virtual void Iceman_Inclife(){
   };
    virtual void rob_weapon(Soldier *p,int hour,int i){
   };
    friend class Headquarter;//声明友元
    friend class MAP;
    friend class Wolf;
};

class Dragon:public Soldier{
   
private:
    //double fmorale;
public:
    Dragon(Headquarter *p,int _kind):Soldier(p,_kind){
   
        Weapon_list.push_back(Weapon(pHeadquarter->total_soldier % WEAPON_NUM));
        Weapon_num = 1;
        Weapon_list[0].force = get_weapon_force(Weapon_list[0].kind); 
    }
    virtual void Print(int hour){
   
        Soldier::Print(hour);
    }
    virtual void yell(int hour,int i);
};

class Ninja:public Soldier{
   
public:
    Ninja(Headquarter *p,int _kind):Soldier(p,_kind){
   
        Weapon_list.push_back(Weapon(pHeadquarter->total_soldier % WEAPON_NUM));
        Weapon_list.push_back(((pHeadquarter->total_soldier +1) % WEAPON_NUM));
        Weapon_num = 2;
        Weapon_list[0].force = get_weapon_force(Weapon_list[0].kind);
        Weapon_list[1].force = get_weapon_force(Weapon_list[1].kind);
    }
    virtual void Print(int hour){
   
        Soldier::Print(hour);
    }
};

class Iceman:public Soldier{
   
public:
    Iceman(Headquarter *p,int _kind):Soldier(p,_kind){
   
        Weapon_list.push_back(Weapon(pHeadquarter->total_soldier % WEAPON_NUM));
        Weapon_num = 1;
        Weapon_list[0].force = get_weapon_force(Weapon_list[0].kind);
    }
    virtual void Print(int hour){
   
        Soldier::Print(hour);
    }
    virtual void Iceman_Inclife(){
   
        life -= life / 10;
    }
};

class Lion:public Soldier{
   
private:
    int Loyalty;
public:
    static int K;
    Lion(Headquarter *p,int _kind):Soldier(p,_kind){
   
        Loyalty = pHeadquarter->total_life;
        Weapon_list.push_back(Weapon(pHeadquarter->total_soldier % WEAPON_NUM));
        Weapon_num = 1;
        Weapon_list[0].force = get_weapon_force(Weapon_list[0].kind);
    }
    virtual void Print(int hour){
   
        Soldier::Print(hour);
        printf("Its loyalty is %d\n",Loyalty);
    }
    virtual int return_Lion_loyalty(){
   
        return Loyalty;
    }
    virtual void Lion_Incloyalty(){
   
        Loyalty -= K;
    }
};

class Wolf:public Soldier{
   
public:
    Wolf(Headquarter *p,int _kind):Soldier(p,_kind){
   
        Weapon_num = 0;
    }
    virtual void Print(int hour){
   
        Soldier::Print(hour);
    }
    virtual void rob_weapon(Soldier *p,int hour,int i);
};

void Soldier::Print(int hour){
   
    char Color[10];
    if(pHeadquarter->color == 0) strcpy(Color,"red");
    else strcpy(Color,"blue");
    printf("%03d:00 %s %s %d born\n",hour,Color,Name_of_soldier[kind],pHeadquarter->total_soldier);
}

int Soldier::get_weapon_force(int weapon_kind){
   
    if(weapon_kind == SWORD ) return Soldier::Force_of_soldier[kind] / 5;
    if(weapon_kind == BOMB) return Soldier::Force_of_soldier[kind] * 4 / 10;
    else return Soldier::Force_of_soldier[kind] * 3 / 10;
}
void Soldier::Printwin(Soldier *p,int hour,int i){
   
    if(pHeadquarter->color == RED){
   
        printf("%03d:40 red %s %d killed blue %s %d in city %d remaining %d elements\n"
                ,hour,Soldier::Name_of_soldier[kind],No,Soldier::Name_of_soldier[p->kind],p->No,i,life);
    }
    else{
   
        printf("%03d:40 blue %s %d killed red %s %d in city %d remaining %d elements\n"
                ,hour,Soldier::Name_of_soldier[kind],No,Soldier::Name_of_soldier[p->kind],p->No,i,life);
    }
}

int Soldier::count_weapon(int kind)
  • 6
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值