人工智能设计是大多数计算机游戏设计的一个重要组成部分,其最为主要的作用是塑造一个虚拟的玩家形象与游戏之中的真实玩家竞技或交流。目前在技术上说,大部分游戏之中的人工智能设计工作可以归结为有限状态机的设计。本文之中提到的这种棋类游戏,其状态机结构清晰简单,固可以方便的构建出仿真环境,接着用遗传算法推演出人工智能就比较简单了。

其实这个东西目前还没有完成,但是我约摸着其输出的几个样本已经可以作为这个游戏的人工智能使用了,并且根据在演化过程之中得分等级,可以设计出不同难度的人工智能,之前说的那么正经,唉,其实就是发出来装一下逼,而且我现在不想理这烂摊子了。

//避免C标准库函数在Visual C++之中可忽略的警告
//请尽量使用DEV C++编译 
#define _CRT_SECURE_NO_WARNINGS
//头文件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <time.h>
#include <conio.h>
//猿人DNA序列,对棋局所有情况的列表
#define APE_DNA_LEN (9 * 8 * 7 * 6 * 5 * 4)
//基因突变宽度
#define APE_DNA_MUTATION_WIDTH 4000
//每一次棋局允许的步数
#define STEP_NUMBER 1000
//人口,或者说猴口
#define POPULATION_NUMBER 200
//下一代遗传样本对数
#define SEED_NUMBER 20
//繁殖能力
#define REPRODUCTION_ABILITY ( POPULATION_NUMBER / SEED_NUMBER )
//分数标志,用于初始化分数表,表示该位置分数没有登记
#define SCORE_FLAG (STEP_NUMBER * -5.0 * 2.0)
//失效分数,对弈之中若对方胜利的话,己方分数则失效
#define SCORE_FAIL (STEP_NUMBER * -5.0 * 3.0)
//猿猴类型
typedef uint8_t * ape;
//种群类型
typedef ape population[POPULATION_NUMBER];
//棋盘上的坐标类型
typedef struct coordinate coordinate;
struct coordinate{
uint8_t x;
uint8_t y;
};
//棋盘信息描述类型,六个坐标分别指明了上下方玩家的棋子坐标
typedef coordinate checkerboard_inf[6];
//分数项目类型,包含猿猴ID以及对弈分数
typedef struct score_item score_item;
struct score_item{
int id;
double score;
};
//根据棋盘信息显示详细的棋盘情况
void display_checkerboard(const checkerboard_inf cinf){
char checkerboard[3][3][3];
int x, y;
//棋盘准备
for (y = 0; y < 3; y++){
for (x = 0; x < 3; x++){
strcpy(checkerboard[y][x], "  ");
}
}
//绘制上方玩家棋子
strcpy(checkerboard[cinf[0].y][cinf[0].x], "德");
strcpy(checkerboard[cinf[1].y][cinf[1].x], "意");
strcpy(checkerboard[cinf[2].y][cinf[2].x], "日");
//绘制下方玩家棋子
strcpy(checkerboard[cinf[3].y][cinf[3].x], "美");
strcpy(checkerboard[cinf[4].y][cinf[4].x], "苏");
strcpy(checkerboard[cinf[5].y][cinf[5].x], "中");
//绘制到屏幕
for (y = 0; y < 3; y++){
for (x = 0; x < 3; x++){
printf("%s", checkerboard[y][x]);
}
printf("\n");
}
printf("\n");
}
//新建一个毫无智商可言的猿猴
ape new_ape(){
ape nape = (ape)malloc(APE_DNA_LEN);
int i;
for (i = 0; i < APE_DNA_LEN; i++){
//毫无智商可言的猿猴其行为方式是随机的
nape[i] = rand() % 24;
}
return nape;
}
//杀死一个猿猴
void kill_ape(ape kape){
free(kape);
}
//初始化种群
void population_init(population pltn){
int i;
for (i = 0; i < POPULATION_NUMBER; i++){
pltn[i] = new_ape();
}
}
//灭绝种群
void population_kill(population pltn){
int i;
for (i = 0; i < POPULATION_NUMBER; i++){
kill_ape(pltn[i]);
}
}
//让猿猴根据当前棋盘情况回答如何移动自己的棋子
uint8_t ape_answer(const ape iape, const checkerboard_inf inf){
uint32_t id = 0;
id += (inf[0].y * 3 + inf[0].x) * (APE_DNA_LEN / 9);
id += (inf[1].y * 3 + inf[1].x) * (APE_DNA_LEN / 9 / 8);
id += (inf[2].y * 3 + inf[2].x) * (APE_DNA_LEN / 9 / 8 / 7);
id += (inf[3].y * 3 + inf[3].x) * (APE_DNA_LEN / 9 / 8 / 7 / 6);
id += (inf[4].y * 3 + inf[4].x) * (APE_DNA_LEN / 9 / 8 / 7 / 6 / 5);
id += (inf[5].y * 3 + inf[5].x) * (APE_DNA_LEN / 9 / 8 / 7 / 6 / 5 / 4);
return iape[id];
}
//移动棋子
int8_t moving_pawn(checkerboard_inf cinf, uint8_t code){
//被选中准备移动的棋子ID
uint8_t pawn_id = code / 8;
//棋盘视角总在下方玩家,固为3 + pawn_id
coordinate crdnt = cinf[3 + pawn_id];
//计算出行为值
uint8_t action = code % 8;
//区间平移,0~7 转换为 1~8
action++;
int8_t xoft, yoft;
xoft = action % 3 - 1;
yoft = action / 3 - 1;
//置换行为项目,确保禁止弃权移动
if (xoft + yoft == 0){
xoft = -1;
yoft = -1;
}
int8_t x, y;
//计算出目标坐标
x = crdnt.x + xoft;
y = crdnt.y + yoft;
//如果目标坐标出界,扣5分
if ((x < 0 || x > 2) ||
(y < 0 || y > 2)){
return -5;
}
//如果发生了斜向运动
if (xoft != 0 && yoft != 0){
//如果斜向运动并没有经过棋盘中心,那么这是不合法的,扣5分
if ( !((x == 1 && y == 1) || (crdnt.x == 1 && crdnt.y ==1)) ){
return -5;
}
}
int i;
for (i = 0; i < 6; i++){
//如果目标坐标上有棋子,扣5分
if (cinf[i].x == x &&
cinf[i].y == y){
return -5;
}
}
//棋子移动合法,应用目标坐标
cinf[3 + pawn_id].x = x;
cinf[3 + pawn_id].y = y;
//棋子移动合法,不扣分
return 0;
}
//反转棋盘视角
void change_view(checkerboard_inf scinf, const checkerboard_inf dcinf){
scinf[0] = dcinf[3];
scinf[1] = dcinf[4];
scinf[2] = dcinf[5];
scinf[3] = dcinf[0];
scinf[4] = dcinf[1];
scinf[5] = dcinf[2];
scinf[0].y = 2 - scinf[0].y;
scinf[1].y = 2 - scinf[1].y;
scinf[2].y = 2 - scinf[2].y;
scinf[3].y = 2 - scinf[3].y;
scinf[4].y = 2 - scinf[4].y;
scinf[5].y = 2 - scinf[5].y;
}
//根据当前棋盘信息判断是否获胜
int is_win(checkerboard_inf cinf){
coordinate crdnt1, crdnt2, crdnt3;
crdnt1 = cinf[3];
crdnt2 = cinf[4];
crdnt3 = cinf[5];
if (crdnt1.x + crdnt2.x + crdnt3.x == 3 &&
crdnt1.y + crdnt2.y + crdnt3.y == 3){
return 1;
}
else{
return 0;
}
}
//两个猿猴下棋,基本调用
void ape_pk_base(const ape ape1, const ape ape2, double * pscore1, double * pscore2){
//两个猿猴的分数
double score1 = 0.0, score2 = 0.0;
//开局棋盘状况
checkerboard_inf cinf1 = {
{ 0, 0 },
{ 1, 0 },
{ 2, 0 },
{ 0, 2 },
{ 1, 2 },
{ 2, 2 }
};
//视角置换棋盘
checkerboard_inf cinf2;
int i;
for (i = 0; i < STEP_NUMBER; i++){
uint8_t code;
int8_t num1, num2;
//获得先手行为代码
code = ape_answer(ape1, cinf1);
num1 = moving_pawn(cinf1, code);
score1 += num1;
//如果先手获得胜利
if (is_win(cinf1)){
score1 += ( STEP_NUMBER * 5.0 * 4.0 ) / (i + 1);
score2 = SCORE_FAIL;
break;
}
//display_checkerboard(cinf1);
//改变棋局视角为后手
change_view(cinf2, cinf1);
//获取后手行为代码
code = ape_answer(ape2, cinf2);
num2 = moving_pawn(cinf2, code);
score2 += num2;
//如果后手获得胜利
if (is_win(cinf2)){
score2 += (STEP_NUMBER * 5.0 * 4.0) / (i + 1);
score1 = SCORE_FAIL;
break;
}
//display_checkerboard(cinf2);
//如果已经陷入愚蠢的死水境地,那么则没必要继续进行下去
if (num1 == -5 && num2 == -5){
//预算分数
score1 += -5.0 * (STEP_NUMBER - i - 1);
score2 += -5.0 * (STEP_NUMBER - i - 1);
break;
}
//改变棋局视角为先手
change_view(cinf1, cinf2);
}
*pscore1 = score1;
*pscore2 = score2;
//printf("%lf %lf\n", *pscore1, *pscore2);
}
//两个猿猴下棋,相互先手,上层调用
void ape_pk(const ape ape1, const ape ape2, double * pscore1, double * pscore2){
double score1, score2, score3, score4;
//猿猴1先手
ape_pk_base(ape1, ape2, &score1, &score2);
//猿猴2先手
ape_pk_base(ape2, ape1, &score4, &score3);
//无论先后手,只要有任意一局分数失效,分数平均数也就失效
if (score1 == SCORE_FAIL || score3 == SCORE_FAIL){
*pscore1 = SCORE_FAIL;
}
else{
*pscore1 = (score1 + score3) / 2;
}
if (score2 == SCORE_FAIL || score4 == SCORE_FAIL){
*pscore2 = SCORE_FAIL;
}
else{
*pscore2 = (score2 + score4) / 2;
}
//printf("%lf %lf\n", *pscore1, *pscore2);
}
//优选随机产生器
int population_rand(){
int n = 0;
while (1){
//概率分母为递增数列
if (rand() % 2 == 0){
break;
}
else{
n++;
}
}
return n;
}
//32位的随机数产生函数
uint32_t rand_32(){
//基因分裂点
uint32_t num;
uint8_t a, b, c, d;
//用于构成32位的随机数
a = rand() % 256;
b = rand() % 256;
c = rand() % 256;
d = rand() % 256;
num = 0;
num |= a;
num <<= 8;
num |= b;
num <<= 8;
num |= c;
num <<= 8;
num |= d;
return num;
}
//两个猿猴交配,产生下一代
void ape_childbirth(const ape father, const ape mother, ape children){
//基因分裂点
uint32_t cutpt;
cutpt = rand_32();
//切割点偏移范围为 1~APE_DNA_LEN-1
cutpt %= (APE_DNA_LEN - 1);
cutpt++;
uint32_t i;
for (i = 0; i < cutpt; i++){
children[i] = father[i];
}
for (i = cutpt; i < APE_DNA_LEN; i++){
children[i] = mother[i];
}
//根据基因突变宽度进行基因突变
for (i = 0; i < APE_DNA_MUTATION_WIDTH; i++){
children[rand_32() % APE_DNA_LEN] = rand() % 24;
}
}
//依据当前种群诞生下一代种群
void population_birth(const population pltn, score_item score_list[], population npltn){
int i, j, p = 0;
for (i = 0; i < SEED_NUMBER; i++){
ape father, mother;
//优选随机选中父母
father = pltn[score_list[population_rand()].id];
mother = pltn[score_list[population_rand()].id];
//根据繁殖能力生育
for (j = 0; j < REPRODUCTION_ABILITY; j++){
if (j % 2 == 0){
ape_childbirth(father, mother, npltn[p++]);
}
else{
ape_childbirth(mother, father, npltn[p++]);
}
}
}
}
double max_score = -10000000.0; 
//种群竞争
void population_competition(const population pltn, population npltn){
//分数表
double * * score_table;
//分配一级索引
score_table = (double * *)malloc( sizeof(double *) * POPULATION_NUMBER);
int i, j;
//分配二级索引
for (i = 0; i < POPULATION_NUMBER; i++){
score_table[i] = (double *)malloc(sizeof(double) * POPULATION_NUMBER);
//分数表默认全部设置为分数标志,表示分数表目前全部为空
for (j = 0; j < POPULATION_NUMBER; j++){
score_table[i][j] = SCORE_FLAG;
}
}
int x, y;
for (y = 0; y < POPULATION_NUMBER; y++){
for (x = 0; x < POPULATION_NUMBER; x++){
//自己不需要和自己对弈
if (x != y){
//如果该项目并未登记
if (score_table[y][x] == SCORE_FLAG){
double score1, score2;
//让选中的两个猿猴进行对弈,产生分数
ape_pk(pltn[y], pltn[x], &score1, &score2);
score_table[y][x] = score1;
score_table[x][y] = score2;
}
}
}
}
//分数列表
score_item score_list[POPULATION_NUMBER];
for (y = 0; y < POPULATION_NUMBER; y++){
score_list[y].id = y;
//分数和
double score_sum = 0.0;
int count = 0;
for (x = 0; x < POPULATION_NUMBER; x++){
//如果是有效分数则累加,每一行有一个非有效分数
if (score_table[y][x] != SCORE_FLAG &&
score_table[y][x] != SCORE_FAIL){
score_sum += score_table[y][x];
//记录累加了多少个得分
count++;
}
}
//依次释放二级索引
free(score_table[y]);
if (count == 0){
count = 1;
}
score_list[y].score = score_sum / count;
}
//释放一级索引
free(score_table);
//对分数项目表进行选择排序,分数降序排列
for (y = 0; y < POPULATION_NUMBER - 1; y++){
int max = y;
for (x = y + 1; x < POPULATION_NUMBER; x++){
if (score_list[max].score < score_list[x].score){
max = x;
}
}
if (max != y){
score_item tmp_item;
tmp_item = score_list[max];
score_list[max] = score_list[y];
score_list[y] = tmp_item;
}
}
if( score_list[0].score > max_score ){
FILE * fp; 
char file_name[100];
unsigned long int n;
max_score = score_list[0].score;
n = (unsigned long int)max_score;
sprintf(file_name,"%ld.dat",n);
fp = fopen(file_name, "wb");
fwrite(pltn[score_list[0].id], APE_DNA_LEN, 1, fp );
fclose(fp);
}
printf( "%lf\t%lf\n", max_score, score_list[0].score );
population_birth(pltn, score_list, npltn);
}
int main(int argc, char * argv[]){
//初始化伪随机数种子
srand((unsigned int)time(NULL));
population pltn;
population npltn;
population_init(pltn);
population_init(npltn);
while (1){
population_competition(pltn, npltn);
population_competition(npltn, pltn);
}
population_kill(npltn);
population_kill(pltn);
return 0;
}

转载于:https://www.cnblogs.com/jimaojin/p/5234645.html

利用遗传算法演化一个棋类游戏的人工智能相关推荐

  1. 利用多线程写一个龟兔赛跑游戏

    代码地址:src · master · 小肖在路上 / ider · GitCode 直接克隆就好了 我来说一下大致思路: 1.利用swing创建窗口,开始按钮,复原按钮,乌龟图片.兔子图片 2.点击 ...

  2. 利用python做一个小游戏_如何使用python做一个简单的猜数字的小游戏

    1 首先小编先打开IDLE,如下图: 2 然后这里点击菜单栏的'File',然后点击菜单"New File",如下图: 3 然后我们就在idle中新建了一个python文件,如下图 ...

  3. 利用C语言巧妙实现棋类游戏——三子棋

    小游戏:三子棋用C语言实现 你是否学完了C语言的函数.数组.选择结构.循环结构苦于没有实战小项目巩固自己所学的知识呢,今天小程序猿就给大家带来了一个游戏的小游戏--三子棋,利用C语言实现的,希望对大家 ...

  4. 用遗传算法加强足球游戏的人工智能

    终于等够了三个月,杂志的约定已经到期,可以把这篇文章发表到网络跟大家分享.本文原发表于<游戏创造>杂志www.chinagcn.com,如蒙转载,请保留原文和本声明完整,并注明转载自恋花蝶 ...

  5. python做一个小游戏_利用python做个小游戏

    从本期开始,我们将利用几天的时间用python来做个小游戏,当然,在做小游戏之前,我们必须学会一个做小游戏的第三方库--pygame.可能有人会说,python不擅长或者说不适合用来做游戏,的确是这样 ...

  6. 利用pgzero做一个接球的小游戏

    利用pgzero做一个接球的小游戏 说明 pgzero为python的一个用于游戏制作的库,它基于pygame模块 可用如下命令去安装 pip install pygame pip install p ...

  7. android打地鼠设计报告,android开发中利用handler制作一个打地鼠小游戏

    android开发中利用handler制作一个打地鼠小游戏 发布时间:2020-11-25 15:21:11 来源:亿速云 阅读:136 作者:Leah 这期内容当中小编将会给大家带来有关androi ...

  8. 利用Python做一个简单的对战小游戏

    利用Python做一个简单的文字对战小游戏 一.游戏介绍 1.大体介绍:文字版的对战小游戏,可以利用Python随机生成两个角色,角色带有各自的血量和攻击值两个指标.两人在对战时同时攻击对方,同时造成 ...

  9. python抽奖游戏_利用Python写一个抽奖程序,解密游戏内抽奖的秘密

    原标题:利用Python写一个抽奖程序,解密游戏内抽奖的秘密 前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者: 极客 ...

最新文章

  1. Android:解决Gradle DSL method not found: 'runProguard()' 问题
  2. docker 容器基本的操作
  3. 复合主键@IdClass
  4. Java减少依赖_去掉JAVA部分依赖的事例
  5. 工作总结9:vue处理token
  6. python中while语句是_如何在Python中使用while语句[适合初学者]
  7. 你的DNA都会玩摇滚了,你却还是个音痴
  8. 一起玩转SQL Server 2012 下的分析服务
  9. ivona tts语音合成引擎_高端玩家!树莓派 + Node.js 实现语音机器人
  10. 京东私有云建设:挑战与应对之道
  11. python保存与加载LGBM模型,并解决报错TypeError: Need at least one training dataset or model file or model string..
  12. 《计算机网络》第四章:介质访问控制(The Medium Access Control Sublayer)
  13. 思科决定将不修复路由器中的这70多个漏洞
  14. 图:出场顺序号码随机抽取及公开展示,并行随机抽取多个题目号码及公开展示-软件原型设计
  15. 课改 计算机 论文,计算机论文 计算机应用课改分析
  16. 时间序列(二):时间序列平稳性检测
  17. C# WinForm制作登录界面
  18. 2018前端走向全栈,Nodejs快速入门视频教程
  19. 这个被上帝抛弃的国家,创立了全球一半的科技公司
  20. 《我的极品媳妇》方志强 王亚欣 小说读后感

热门文章

  1. group by和order by在springboot中连用03
  2. 小程序入门学习13--云函数与数据库02
  3. 一个简单HTML标签marquee实现动态滚动条
  4. c 语言从文件中读取字符串数组,C从文本文件读取到数组/字符串
  5. 相信阿里只会PPT的“假专家”,随意做数字化转型,活该失败
  6. 不写代码,可视化堪比python,领导满意的报表工具,原来是这样?
  7. 上传项目到gitOsChina
  8. pdf sdk for android,Android 自带PDF SDK
  9. android 生成apk名字自动已,Jenkins打包android应用时自动签名apk详解
  10. python 缺省参数_week04_python函数缺省值