1. 程式人生 > >五子棋人機對戰完整程式碼

五子棋人機對戰完整程式碼

一,五子棋棋盤

棋盤正中一點為“天元”。棋盤兩端的橫線稱端線。棋盤左右最外邊的兩條縱線稱邊線。從兩條端線和兩條邊線向正中發展而縱橫交叉在第四條線形成的四個點稱為“星”。

以持黑方為準,棋盤上的縱軸線從左到右用英文字母A~O標記。橫行線從近到遠用阿拉伯數字1~15標記。縱橫軸上的橫縱線交叉點分別用橫縱線標記的名稱合寫成。如“天元”H8,四個“星”分別為D4、D12、L12、L4等。


二,五子棋比賽規則

1,行棋順序
黑先、白後,從天元開始相互順序落子。
2,判斷勝負
最先在棋盤橫向、豎向、斜向形成連續的相同色五個棋子的一方為勝。
黑棋禁手判負,白棋無禁手。黑棋禁手包括三三禁手,四四禁手,長連禁手。
如分不出勝負,則定為平局。
3, 棋子表示
為了使自已與對手看得更清楚,剛落下的子應區別表示,例如
白方正常子:○    白方剛落下之子:△
黑方正常字:●    黑方剛落下字:▲

三,重要棋型解釋

1,五連:五顆同色棋子連在一起

2,活四:有2個成五點的四顆棋子

3,衝四:有1個成五點的四顆棋子

4,活三:可以形成活四的三顆棋子

四,禁手規則

1,三三禁手

由於黑方落一子,同時形成二個或二個以上黑方活三的局面

2,四四禁手

由於黑方落一子,同時形成二個或二個以上黑方四(活四或者衝四)的局面

3,長連禁手

由於黑方落一子,形成六個或者六個以上的同色連續棋子

五,關鍵技術解釋

1,flat技術

通過dx和dy這2個常數陣列,存下8個方向的向量,就可以把棋型判斷、禁手判斷等二維問題化作一維問題

通過for迴圈即可遍歷每個方向,使得程式碼變得非常簡潔

2,棋型判斷和禁手判斷

對於任何一個落子的位置,要獨立的判斷形成了幾個活四,幾個衝四,幾個活三。

三三禁手和四四禁手直接根據三種棋型的數量判斷即可,長連禁手單獨判斷,很簡單。

3,打分機制

為了降低計算量,採取對每個落子位置單獨進行打分的方法。

落子之後計算棋型,活四計1000分,衝四和活三都計100分。

另外,落子位置的8個鄰居中,只要是有落子的位置,無論是黑是白,都計1分。

(對於邊界上的點,只有5個或者3個鄰居,為了程式設計方便,棋盤本身應該用一圈空格包圍)

這樣的話,對於0分的點直接忽略,即可大大增加剪枝效率。

因為0分的點是絕對不下的,所以禁手點直接算0分即可。

雖然規則允許下禁手點,但是禁手判負,所以AI認為無論黑方不會下禁手點是合理的(無論黑方是AI還是玩家)

4,AI演算法

採取三層的極大極小演算法

六,程式碼:

#include <stdio.h>
#include<string>
#include<windows.h>
#define N 15
#define samekeysame(row + dx[u] * i, col + dy[u] * i, key)
#define sumkaddfor (i = 1; samekey; i++)sumk++; 
#define sumksubfor (i = -1; samekey; i--)sumk++;
#define offif(!inboard(row + dx[u] * i, col + dy[u] * i) || p[row + dx[u] * i][col + dy[u] * i] != 0)continue;
#define temp0if (tempp == 0){p[i][j] = 0;continue;}

int p[N + 2][N + 2]; //0空1黑2白  1●2○ -1▲-2△
int s = 0, ais = 1, s0;//s是輪到誰下,s=1,2,s=1是ai下,s=2是玩家,s=s0是黑方下,否則是白方下
bool is_end = false;
int dx[8] = { 1, 1, 0, -1, -1, -1, 0, 1 }; //flat技術
int dy[8] = { 0, 1, 1, 1, 0, -1, -1, -1 };//(dx,dy)是8個方向向量
int manu[2][300], manukey=0;

int out(int i, int j)
{
	if (p[i][j] == 1)return printf("●");
	if (p[i][j] == 2)return printf("○");
	if (p[i][j] == -1)return printf("▲");
	if (p[i][j] == -2)return printf("△");
	if (i == N)
	{
		if (j == 1)return printf("┏");
		if (j == N)return printf("┓");
		return printf("┯");
	}
	if (i == 1)
	{
		if (j == 1)return printf("┗");
		if (j == N)return printf("┛");
		return printf("┷");
	}
	if (j == 1)return printf("┠");
	if (j == N)return printf("┨");
	return printf("┼");
}
void DrawBoard()//畫棋盤
{
	system("cls");
	int row = 0, col = 0, keyr = 0, keyc = 0;
	char alpha = 'A';
	printf("\n\n\n     ");
	for (col = 1; col <= N; col++)printf("%c ", alpha++);
	for (row = N; row >= 1; row--)
	{
		printf("\n   %2d", row);
		for (col = 1; col <= N; col++)
		{
			out(row, col);
			if (p[row][col] < 0)keyr = row, keyc = col;
		}
		printf("%d", row);
	}
	alpha = 'A';
	printf("\n     ");
	for (col = 1; col <= N; col++)printf("%c ", alpha++);
	printf("\n\n");
	if (s0 == ais)printf("  AI執黑,玩家執白\n");
	else printf("  AI執白,玩家執黑\n");
	alpha = 'A';
	if (keyr)printf("  最後落子位置:%c%d\n", alpha + keyc - 1, keyr);
}

void init()
{
	system("color f0");
	printf("輸入1或者2進行選擇\n1,AI執黑先行\n2,玩家執黑先行\n");
	scanf_s("%d", &s);
	if (s != 1 && s != 2)return init();
	s0 = s;
	int i, j;
	for (i = 0; i <= N + 1; i++)for (j = 0; j <= N + 1; j++)p[i][j] = 0;//以空格包圍棋盤	
	DrawBoard();
	for (j = 0; j < 300; j++)manu[0][j] = manu[1][j] = 0;
}

bool inboard(int row, int col)//是否在棋盤內
{
	if (row <1 || row > N)return false;
	return col >= 1 && col <= N;
}

int same(int row, int col, int key)//判斷2個棋子是否同色
{
	if (!inboard(row, col))return false;
	return (p[row][col] == key || p[row][col] + key == 0);
}

int num(int row, int col, int u)//座標(row,col),方向向量u
{
	int i = row + dx[u], j = col + dy[u], sum = 0, ref = p[row][col];
	if (ref == 0)return 0;
	while (same(i, j, ref))sum++, i += dx[u], j += dy[u];
	return sum;
}

int live4(int row, int col)//活4的數量
{
	int key = p[row][col], sum = 0, i, u;
	for (u = 0; u < 4; u++)//4個方向,每個方向最多1個
	{
		int sumk = 1;
		sumkadd off sumksub off
		if (sumk == 4)sum++;
	}
	return sum;
}
int chong4(int row, int col)//衝4的數量
{
	int key = p[row][col], sum = 0, i, u;
	for (u = 0; u < 8; u++)//8個方向,每個方向最多1個
	{
		int  sumk = 0;
		bool flag = true;
		for (i = 1; samekey || flag; i++)//成五點的方向
		{
			if (!samekey)
			{
				if (flag&&p[row + dx[u] * i][col + dy[u] * i])sumk -= 10;
				flag = false;
			}
			sumk++;
		}
		if (!inboard(row + dx[u] * --i, col + dy[u] * i))continue;
		sumksub
		if (sumk == 4)sum++;
	}
	return sum - live4(row, col) * 2;
}
int live3(int row, int col)//活3的數量
{
	int key = p[row][col], sum = 0, i, u;
	for (u = 0; u < 4; u++)//三連的活三
	{
		int sumk = 1;
		sumkadd offi++; off;
		sumksub offi++; off;
		if (sumk == 3)sum++;
	}
	for (u = 0; u < 8; u++)//8個方向,每個方向最多1個非三連的活三
	{
		int  sumk = 0;
		bool flag = true;
		for (i = 1; samekey || flag; i++)//成活四點的方向
		{
			if (!samekey)
			{
				if (flag&&p[row + dx[u] * i][col + dy[u] * i])sumk -= 10;
				flag = false;
			}
			sumk++;
		}off
		if (p[row + dx[u] * --i][col + dy[u] * i] == 0)continue;
		sumkadd off
		if (sumk == 3)sum++;
	}
	return sum;
}
bool overline(int row, int col)//長連禁手
{
	bool flag = false;
	int u;
	for (u = 0; u < 4; u++)if (num(row, col, u) + num(row, col, u + 4) > 4)flag = true;
	return flag;
}
bool ban(int row, int col)//判斷落子後是否成禁手
{
	if (same(row, col, 2))return false;//白方無禁手
	bool flag = live3(row, col) > 1 || overline(row, col) || live4(row, col) + chong4(row, col) > 1;
	return flag;
}

bool end_(int row, int col)//(row,col)處落子之後是否遊戲結束
{
	int u;
	for (u = 0; u < 4; u++)if (num(row, col, u) + num(row, col, u + 4) >= 4)is_end = true;
	if (is_end)return true;
	is_end = ban(row, col);
	return is_end;
}

void go(int row, int col)//落下一子
{
	if (s == s0)p[row][col] = -1; //標出最新下的棋
	else p[row][col] = -2;
	for (int i = 0; i <= N; i++)for (int j = 0; j <= N; j++) //取消上一個最新棋的標識
	{
		if (i == row && j == col)continue;
		if (p[i][j] < 0)p[i][j] *= -1;
	}
	DrawBoard();
	if (ban(row, col))
	{
		if (s0 == 1)printf("玩家勝");
		else printf("AI勝");
		Sleep(10000);
	}
	if (end_(row, col))
	{
		if (s == ais)printf("AI勝");
		else printf("玩家勝");
		Sleep(10000);
	}
	manu[0][manukey] = row, manu[1][manukey++] = col;
}

bool ok(int row, int col)//能否落子
{
	return inboard(row, col) && (p[row][col] == 0);
}

int point(int row, int col)//非負分值
{
	if (ban(row, col))return 0;//禁手0分
	
            
           

相關推薦

五子棋人機完整程式碼

一,五子棋棋盤棋盤正中一點為“天元”。棋盤兩端的橫線稱端線。棋盤左右最外邊的兩條縱線稱邊線。從兩條端線和兩條邊線向正中發展而縱橫交叉在第四條線形成的四個點稱為“星”。以持黑方為準,棋盤上的縱軸線從左到右用英文字母A~O標記。橫行線從近到遠用阿拉伯數字1~15標記。縱橫軸上的橫

java五子棋人機演算法分析

        五子棋人機對戰採用的演算法,目前大都是搜尋樹演算法,用一棵樹來表示棋局發展的 種種可能性,這種樹叫做博弈樹(對局樹)。根節點表示對局的開始狀態,每一種可 能的走法造成的結果作為其子節點,而對每一個這樣的子節點,考慮另一方的各種 可能應對,作為下一層的子節點,這

C# winform 簡單五子棋 200行程式碼實現人機

1、功能需求接上篇博文,本文描述簡單人機對戰實現過程,只是簡單實現考慮走一步策略,如果要想實現走多步策略,可以在本文估值演算法的基礎上用極大極小值配合剪枝演算法,實現考慮多步策略,這樣ai會顯得更加聰明,後期如果有時間完善,會更新程式碼。2、介面設計參考上一篇博文的介面。3、

AI智慧人機五子棋(Java實現圖形介面)

簡述 這是本學期上完Java課後老師給出的課程設計題目,目的是:熟悉與掌握GUI程式設計;實現五子棋棋盤和棋子的繪製;實現遊戲AI以及對二維陣列的使用。 介面效果圖 電腦先行,玩家輸贏圖: 玩家先行,玩家輸贏圖: 整體設計 介面設

五子棋專案的實現(三)人機類的具體設計

在之前描述了博弈樹演算法的思想,現在則是關鍵類的設計實現。在具體的過程中我們先要設計一個遍歷棋型演算法,來遍歷整個棋盤中的各種棋型   通過最後返回值的不同,來確定不同的棋型 當中有評估函式對當前的棋型進行打分。再選取區域性最優的幾個落子點作為下一步擴充套件的節點。 //bwf 棋色

QT五子棋專案詳解之四:AI人機max-min極大極小值博弈演算法

不考慮博弈的演算法怎麼能算是AI呢?max-min極大極小值演算法就是考慮了博弈的演算法。來看一個簡單的例子在這個棋局中,電腦為白旗,白旗走哪一步更好呢,也許使用策略表會告訴你,應該衝4,但是衝4後,玩家就會連成4。這就是考慮了博弈之後,這一步棋就是敗局。這就是為什麼有max

Android實現五子棋遊戲(二) 人機實現

下面簡單介紹一下實現人機對戰的思路以及程式碼實現: 思路 人機對戰的總體思路是通過遍歷所有的無棋子的位置,通過模擬在每個無棋子點落子,並根據其周圍的棋子來獲取該點的兩個優先順序評分: 模擬使用者棋子來獲取一個優先順序評分,用於防守(使用者

Python小遊戲 井字棋(人機,玩家

pan urn utf-8 erl ext print cef () nbsp # -*- coding:utf-8 -*-import timeimport random#井字棋 人機對戰def drawBoard(board): blank_board = '|

c語言實現五子棋人人

利用簡單的c語言基礎 實現最簡單的功能 介面比較醜陋主要是剛學完c的一個小實踐 未使用MFC所以介面沒有很好看 主要目的加強對c語言的理解與運用 同時增加自己的程式碼量   首先要學一些標頭檔案可以看我的部落格前面的文章 要用到到的標頭檔案stdio.h stdlib

websocket實現五子棋聯機

GoBang.html <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <st

"人機":電腦太簡單了,我是射手 skr~skr~skr

9月17日,2018 世界人工智慧大會在上海拉開帷幕。在 SAIL 榜單入圍專案中,我看到了小愛同學、小馬智行、微軟小冰、騰訊覓影等等,這不僅讓我大開了眼界,也不禁讓我感慨 AI 的發展神速。猶記得去年在中國烏鎮圍棋峰會上,AlphaGo 與排名世界第一的世界圍棋冠軍柯潔對戰,以 3 比 0 的總比分獲勝,那

人機(猜拳)

# -*- coding: utf-8 -*-# 人和機器猜拳遊戲寫成一個類,有如下幾個函式:# 1)函式1:選擇角色1 曹操 2張飛 3 劉備# 2)函式2:角色猜拳1剪刀 2石頭 3布 玩家輸入一個1-3的數字# 3)函式3:電腦出拳 隨機產生1個1-3的數字,提示電腦出拳結果# 4)函式4:角色和機器出

井字遊戲 人機 java實現

package com.ecnu.Main; /** * 主函式觸發遊戲 */public class MainApplication { public static void main(String[] args){ TicTacToeGame ticTacToeGame = new TicTacToeG

象棋人機

象棋主要演算法程式碼 Eveluation.h Eveluation.cpp這一對檔案定義並實現了估值核心類。 #if !defined(AFX_EVELUATION_H__2AF7A221_CB28_11D5_AEC7_5254AB2E22C7__INCLUDED_)

五子棋人人實現

在十字交差點畫棋子,有幾種方法,第一種:獲取當前滑鼠點選的x,y值, 遍歷整個棋盤所有的交叉點座標,與其在一定誤差範圍內的交叉點座標即 為落子的座標點。第二種:直接將獲取的x,y座標減去棋盤外圍寬度然後 再除以棋盤每格的寬度,即可得到此座標位於棋盤的那一行那一列,然後 即可

演算法 最近問題完整程式碼分析

#include <iostream> //#include <algorithm> #include<cmath> using namespace std; const int n=10; struct point{

Java swing + socket 寫的一個五子棋網路遊戲

##網路對戰版本的五子棋遊戲,包含服務端和客戶端(c/s模式) ,寫成此文章給需要或想研究的人一些參考(相關程式碼我會放在本文的最後) 遊戲效果圖: ###下載線上客戶端版本試玩: (插播:下面連結下載的客戶端如果登入不了伺服器的話,請直接下載文章最後的程式碼

Java Swing實現的人機三子棋

1.照例,先來看看執行結果吧。 2.演算法思想: 1.普通情況下,當玩家先下時,演算法根據當前情況判斷自己是否能贏。若可以,則直接選擇該情況,否則開始判斷玩家是否有可以贏的位置,若有,則堵死玩家,,若仍沒有,選擇當前最合適的走法,走法較多時,使用隨機

基於Qt Creator實現中國象棋人機, c++實現

這是自己大一學完c++後,在課程實踐中寫過的一個程式,實現象棋人機對戰的演算法還是有點難的, 自己當時差不多也是寫了兩個月左右吧!當時看書又有很多問題得不到解決,所以就在網上找了一個視訊跟著

六子衝棋,六子炮棋,二打一棋,箭棋,炮棋(java單機版)java人機

原始碼及exe檔案帶jre下載: 六子衝   六子衝是流傳於中國民間的一類棋類遊戲。由於這個遊戲對環境的要求不高,孩子們大都是在光滑的地面或石板上畫上方格,以石子或木棍、草節等為棋子,並有簡單的比賽規則:   縱橫各四條直線組成一個正方形棋盤,直線相交的地方為落