大作业:魔兽世界

魔兽世界(三)

在魔兽(三)中,最重要的是加入了武士在城市之间的移动和战斗。
具体题目见:http://cxsjsx.openjudge.cn/hw202006/E/

框架搭建

Cweapon类:用来存放某个武士拥有的武器状态。(包括各种武器分别的数量、总数量、以及pick_weapon成员函数用来为交手时选择武器)
Warrior类:武士类,包含生命值、攻击力、位置、编号、武器状况基本信息。
Dragon类:Warrior类的派生类。
Ninja类:Warrior类的派生类。
Iceman类:Warrior类的派生类。
Lion类:Warrior类的派生类。
Wolf类:Warrior类的派生类。
City类:代表一个城市。包含一个warrior** 类的指针h,h是个长度为4的数组,用来存放当前城市的warrior的指针,在武士移动、战斗过程中十分重要。其中,0位储存当前城市red warrior信息,2位储存临时red信息,1位储存当前blue warrior信息,3位储存临时blue信息。
Battleground类:战场构建。成员变量有Lifetime2、TotalWarrior2、curent2、produce_end2、warrior5
成员函数如下:
void print_time():输出当前时间
void create_warrior(int color):制造武士
void clear():更新战场(临时储存的更替)
bool march():武士移动,返回false即已经到达基地
void escape():lion逃离
void rub_weapon(Warrior* p,Warrior q):胜利者抢夺失败者的武器
void print_weapon(int t):输出武器名
void wolf_rob_weapon(Warrior
p,Warrior*q):wolf抢夺非wolf敌人的武器
void wolf_rub():判断是否需要wolf抢
void fight():战争开始
void headquarter_report():司令部报告
void warrior_report():武士报告
bool check_time():检查是否到达结束时间,返回false即结束
bool run_a_round():运行一个回合

关键函数思路

先看一个回合内函数的运行顺序,run_a_round要是返回false代表游戏结束。

bool run_a_round

bool run_a_round()//运行一个回合
    {
        Minute = 0;
        if (!check_time()) return false;
        create_warrior(0);
        create_warrior(1);
        Minute = 5;
        if (!check_time()) return false;
        escape();
        Minute = 10;
        if (!check_time()) return false;
        if(!march()) return false; //到基地了
        Minute = 35;
        if (!check_time()) return false;
        wolf_rub();
        Minute = 40;
        if (!check_time()) return false;
        fight();
        Minute = 50;
        if (!check_time()) return false;
        headquarter_report();
        Minute = 55;
        if (!check_time()) return false;
        warrior_report();
        Hour++;
        return true;
    }

*** bool march
武士移动至下一个城市的临时位置(还未renew)
关于指针,笔者犯过很多错误,总结来说有以下注意事项
1.防止出现野指针:(直接避免是最好的方法)
a.定义指针要初始化,即使=NULL
b.分配后和使用前要检测,使用时不要越界
c.释放后一定 = NULL
2.delete可以使用在空指针上(系统会自动跳过),但不能使用在野指针上。
注:删除一个指针p(delete p;)实际意思是删除了p所指的目标(变量或对象等),释放了它所占的堆空间,而不是删除p本身(指针p本身并没有撤销,它自己仍然存在,该指针所占内存空间并未释放)。
3.不要让内存泄漏了:比如直接使new 的指针delete前就NULL
但是如果是赋值反而不能delete,笔者写的时候debug很久的一个错误就是clear函数中赋值指针:city[i].h[0]=city[i].h[2];delete city[i].h[2];city[i].h[2]=NULL;2赋值给了0,这时候2直接置NULL也不会内存泄漏,否则会把0指向的空间一起删了。
4.访问指针指向地址是一定要判断是否是NULL

在march函数中移动武士中需要判断是不是Iceman,这时候用typeid函数比较方便确认对象类型(头文件typeinfo):if(typeid(a)==typeid(Iceman))

fight()

fight函数是处理武士战斗的,是本程序最麻烦的也是最重要的过程之一。此处参考了另外一位同学的方法。
题目描述在奇数号城市红武士先进行攻击,偶数号城市蓝武士先进行攻击。利用这一点,对任意城市i,设r为城市/2的余数,即r方先进行攻击,l方受到攻击。若下一次使l攻击,再使r攻击,代码稍显繁琐。这时采用异或运算,每次下一回合攻击时让r和l分别异或1,这时每次都是r进行攻击。
还有一个重要的问题就是有一种特殊的情况就是双方都有武器,但是由于攻击力太低,导致武器的伤害=0(去尾取整),这时候不加以判断就会导致程序TLE死循环了,于是采用局势比较,两个战斗回合双方属性不变就结束战斗。但是每个回合都检查并储存属性信息未免过于费时间,毕竟这种情况是极端情况,于是每8个round检查一次。

void fight()//战争开始
    {
        for (int i = 1; i <= CityNumber; ++i)
        {
            if (city[i].h[0] == NULL || city[i].h[1] == NULL)
                continue;
            int r = i % 2, L = r ^ 1;
            int n[2] = {1, 1}, w, flag = 5, check[4] = {0}, round = 0;
            while (true) //交战至回合结束
            {
                //判断是否局势改变,若不改变则为平局
                r = r ^ 1;//r方进行攻击
                L = L ^ 1;//两者交互
                if (round == 0)
                {
                    if (check[0] == city[i].h[0]->wLifetime && check[1] == city[i].h[1]->wLifetime && check[2] == city[i].h[0]->Weapon.amount && check[3] == city[i].h[1]->Weapon.amount)
                    {
                        flag = 0;//all alive 都还活着
                        break;
                    }
                    check[0] = city[i].h[0]->wLifetime;
                    check[1] = city[i].h[1]->wLifetime;
                    check[2] = city[i].h[0]->Weapon.amount;
                    check[3] = city[i].h[1]->Weapon.amount;
                    round = 8; //8个回合检查一次是否局势不改变了
                }
                round--;
 
                if (city[i].h[L]->Weapon.amount == 0 && city[i].h[r]->Weapon.amount == 0)
                {
                    flag = 0;//alive
                    break;
                }
                if(city[i].h[r]->Weapon.amount == 0)
                    continue;
                w = city[i].h[r]->Weapon.pick_weapon(n[r]); //本轮用的武器编号
                
                int j;
                switch (w) //攻击
                {
                case 0://sword
                    city[i].h[L]->wLifetime -= city[i].h[r]->force / 5; break;
                case 1://bomb
                    j = city[i].h[r]->force * 2 / 5;
                    city[i].h[L]->wLifetime -= j;
                    if(typeid(*city[i].h[r]) != typeid(Ninja))
                        city[i].h[r]->wLifetime -= j / 2;
                    break;
                case 2://arrow
                    city[i].h[L]->wLifetime -= city[i].h[r]->force * 3 / 10; break;
                default:
                    break;
                }
                
                if (city[i].h[L]->wLifetime <= 0 && city[i].h[r]->wLifetime <= 0)
                {
                    flag = 3;//all died
                    break;
                }
                else if (city[i].h[L]->wLifetime <= 0)
                {
                    if (L == 0)
                    {
                        flag = 2; //blue win
                        break;
                    }
                    else
                    {
                        flag = 1; //red win
                        break;
                    }
                }
                else if (city[i].h[r]->wLifetime <= 0)
                {
                    if (r == 0)
                    {
                        flag = 2; //blue win
                        break;
                    }
                    else 
                    {
                        flag = 1; //red win
                        break;
                    }
                }
                else
                    flag=0;//都活着:本来不需要写出来,程序已经保证,但写出来逻辑更清晰
            }
            print_time();
            switch (flag) 
            {
            case 0: //双方都活着——平局
                cout << " both red ";
                city[i].h[0]->print_name();
                cout << " and blue ";
                city[i].h[1]->print_name();
                cout << " were alive in city " << i << endl;
                break;
            case 3: //双方都死了——平局
                cout << " both red ";
                city[i].h[0]->print_name();
                cout << " and blue ";
                city[i].h[1]->print_name();
                cout << " died in city " << i << endl;
                delete city[i].h[0];
                delete city[i].h[1];
                city[i].h[0] = NULL;
                city[i].h[1] = NULL;
                break;
            case 1: //红方胜利
                cout << " red ";
                city[i].h[0]->print_name();
                cout << " killed blue ";
                city[i].h[1]->print_name();
                printf(" in city %d remaining %d elements\n", i, city[i].h[0]->wLifetime);
                rub_weapon(city[i].h[0], city[i].h[1]);
                delete city[i].h[1];
                city[i].h[1] = NULL;
                break;
            case 2: //蓝方胜利
                cout << " blue ";
                city[i].h[1]->print_name();
                cout << " killed red ";
                city[i].h[0]->print_name();
                printf(" in city %d remaining %d elements\n", i, city[i].h[1]->wLifetime);
                rub_weapon(city[i].h[1], city[i].h[0]);
                delete city[i].h[0];
                city[i].h[0] = NULL;
                break;
            default:
                break;
            }
            for (int t = 0; t < 2; ++t)//dragon没有战死就会欢呼
            {
                if (city[i].h[t] != NULL && typeid(*city[i].h[t]) == typeid(Dragon))
                {
                    print_time(); cout << ' ' << HeadquarterName[t] << ' ';
                    city[i].h[t]->print_name();
                    cout << " yelled in city " << i << endl;
                }
            }
 
        }
    }

pick_weapon

本题中还有一个比较麻烦的函数,就是使用序号为n的武器。比较特殊的是需要判断除了sword外所有武器都使用完时需要再次使用sword。

int pick_weapon(int& n) //决定第n次交手应该使用的武器,n已经考虑过经过循环处理为当前武器序号
    {
        //进入这个函数保证amount>0
        bool flag = false;
        if (amount <= n) flag = true;//除了sword以外的武器都已经用完了
        if (n <= a[0])
        {
            ++n;
            if(flag) n = 1;
            return 0;
        }
        else if (n <= a[0] + a[1])
        {
            a[1]--;
            amount--;
            if(flag) n = 1;
            return 1;
        }
        else if (n <= a[3] + a[0] + a[1])
        {
            a[3]--;
            amount--;
            if(flag) n = 1;
            return 2;
        }
        else if (n <= amount)
        {
 
            a[2]--; //2是没有用过的arrow
            a[3]++; //3是用过的arrow
            ++n;
            if(flag) n = 1;
            return 2;
        }
    }

猜你喜欢

转载自www.cnblogs.com/wwk510/p/12503757.html