Java实现简单井字棋
Java第一次实验,老师让做一个井字棋,电脑随机下棋。
然后就想能不能聪明一点,可以判断出走哪一步棋;然后只能做到不会输,还是不够聪明,只能呆板地堵住用户,smartRobot的第三个判断逻辑找不到最佳位置,赢得概率比较小;而且我没事干时,想玩玩这个小游戏找找成就感,但每次都会赢了机器人,所以删删改改了四五次,最后才成。
=======================
可以选择谁先开始,但startGame里的代码更加冗余了。看着就很乱,但没想到好的办法。
smartRobot里的代码全部重写了,比原来更聪明一点了:下在四个角的位置时,能优先选择最佳位置;然后没有最佳位置时,再随便找一空的(随便找空四角位置使用for代替了,比原来更简短)。
然后smartRobot的第一个和第三个判断逻辑,也更聪明一点了。原来判断机器人和判断用户的逻辑,是放在一个for循环里的,但无法找到最佳位置,现在分开了。
机器人先开始时,才能发挥出来新添加的机器人的“小聪明”;但机器人原来的能力发挥不回来,只有用户先开始时才能发挥出原来的能力。所以各有利弊,无论谁先开始都能适应。如果机器人先开始,并且用户第一步棋不是下在四角的位置,那么用户就必输了。其他的情况一般都是平局了。
======================
想到一个因为可以选择谁先开始而导致 startGame 代码冗余的问题的解决方法,就是使用局部内部类。内部类能访问到方法的局部对象。
主逻辑 startGame:
1. 用一个3X3的二维数组,存储棋盘;
2. 用户输入1~9下棋;
3. 判断是否合法,不合法则重新输入;
4. 将1~9转换成二维的坐标 x = (pos-1)/3, y = (pos-1)%3,再令二维数组相应位置为 'O';
5. 判断用户是否胜利,是则退出;再判断是否平局,是则退出;
6. 机器人下棋(根据输入等级,调用不同函数);
7. 打印棋盘显示出用户和机器人下的棋子;
8. 判断机器人是否胜利,是则退出;再判断是否平局,是则退出;都不是返回第1步。
isSuccessful 判断成功的逻辑:
判断所有行、列、对角线是否有连成一条线的,用字符相加的和判断即可
willBeSuccessful判断是否将要成功:
这里判断的是是否有行、列有两个相同棋子和一个空白,用字符相加的和判断。
calculate 计算行列对角线:
使用枚举类,来判断是计算行,还是计算列,还是计算左右对角线;计算行列时,传入一个1~3的数字表示是哪一行那一列。
smartRobot 的第一个判断逻辑:
如果棋子下在箭头指向的那个位置,那么一步棋就可胜利。
机器人先判断自己是否有这样一个位置,有则下在哪个地方,胜利;
方法是尝试填入所有空白地方,每填一次,判断一次 isSuccessful;
如果没有,再判断对方是否有这样一个位置,有则堵住这个地方。
smartRobot 的第三个判断逻辑:
如果棋子下在箭头指向的位置,那么再下一步必会胜利,因为下在了那个地方,第三列、第三行都是两个棋子了,无论对方堵哪里,都会失败。
也是机器人先判断自己是否有这样一个位置,有则下;
调用 willBeSuccessful 判断是否有这样的位置。
没有则再判断对方是否有这样的位置,有则堵住。
smartRobot 的第零个和第二个判断逻辑:
处理四个角和中心的位置,如果用户下在了中心,那么机器人必须至少有两个棋子下在四角位置才能保证不输。
更改了无数次的代码:
import java.util.Arrays;
import java.util.Scanner; public class Experiment_1 {
public static void main(String[] args) {
ThreeChess game = new ThreeChess();
game.startGame(); }
} class ThreeChess{
private char[][] chessBoard = new char[3][3];
private int size = 0; //已经下的棋数
private final int CAPACITY = 9; //总共可下的棋数 ThreeChess(){
for(char[] line : chessBoard){ //初始化棋盘
Arrays.fill(line, ' ');
}
} //【游戏开始】
public void startGame(){
System.out.println("┌───┬───┬───┐");
System.out.println("│ 1 │ 2 │ 3 │");
System.out.println("├───┼───┼───┤");
System.out.println("│ 4 │ 5 │ 6 │");
System.out.println("├───┼───┼───┤");
System.out.println("│ 7 │ 8 │ 9 │");
System.out.println("└───┴───┴───┘");
System.out.println("输入 1 ~ 9 表示要下棋的位置");
System.out.println("O是你的棋子,*是电脑的棋子");
Scanner in = new Scanner(System.in); System.out.print("选择谁先开始:\n\t1.用户\n\t2.机器人\nInput: ");
int whoFirst = in.nextInt();
System.out.print("选择机器人智商:\n\t1. 999+\n\t2. 250\nInput: ");
int level = in.nextInt(); class Play{ //代码重用
//方法返回-1表示退出
int robotPlay(){
if(level == 1)
smartRobot();
else
sillyRobot(); printChessBroad();
if(isSuccessful() == -1) {
System.out.println("机器人胜 (/ □ \\)");
return -1;
}else if (size == CAPACITY){
System.out.println("==游戏平局==");
return -1;
}
return 0;
}
int userPlay(){
int pos;
while(true){
System.out.print("下棋位置: ");
pos = in.nextInt();
if(pos < 1
|| pos > 9
|| chessBoard[(pos - 1) / 3][(pos - 1) % 3] != ' '){
System.out.println("输入错误,重新输入!");
continue;
} else {
chessBoard[(pos - 1)/3][(pos - 1) % 3] = 'O';
size++;
break;
}
} if(isSuccessful() == 1){
printChessBroad();
System.out.println("恭喜,你胜了 ╰(*°▽°*)╯");
return -1;
} else if(size == CAPACITY){
printChessBroad();
System.out.println("==游戏平局==");
return -1;
}
return 0;
}
} Play play = new Play();
if(whoFirst == 2){
while(true){
//1.机器人下棋
if(play.robotPlay() == -1)
return; //2.用户下棋
if(play.userPlay() == -1)
return;
}
} else {
while(true){
//1.用户下棋
if(play.userPlay() == -1)
return; //2.机器人下棋
if(play.robotPlay() == -1)
return;
}
}
} //【机器人下棋】
private void sillyRobot(){ //笨机器人
int l, c;
while(true){
l = (int)(Math.random() * 3);
c = (int)(Math.random() * 3);
if(chessBoard[l][c] == ' '){
chessBoard[l][c] = '*';
break;
}
}
size++;
} private int corner = 2;
private void smartRobot(){ //无法战胜的机器人
if(chessBoard[1][1] == ' '){ //抢占中心位置
chessBoard[1][1] = '*';
size++;
return;
} //1.判断是否可以下一个棋子就胜利(不能放在一起同时判断,否则有可能错误最佳位置)
for(int i = 0; i < 3; i++){
for(int j = 0; j < 3; j++){
if(chessBoard[i][j] == ' '){ //【1】如果这个位置没有棋子,就尝试下载这个地方,看看是否可以胜;
chessBoard[i][j] = '*';
if(isSuccessful() == -1){ //【1】如果胜的话,就下在这个地方了,返回即可;
size++;
return ;
}
else
chessBoard[i][j] = ' ';
}
}
} for(int i = 0; i < 3; i++){
for(int j = 0; j < 3; j++){
//【2】逻辑同【1】
if(chessBoard[i][j] == ' '){
chessBoard[i][j] = 'O'; //【2】否则尝试用户下在这个位置
if(isSuccessful() == 1){ //【2】如果用户下在这个位置会胜利,就占领它。
chessBoard[i][j] = '*';
size++;
return ;
} else
chessBoard[i][j] = ' ';
}
}
} //2.如果用户下在了中间的话,就赶紧占两个四角的位置,才能保证不输。优先级要比第一个低。用户没下在中间也可抢占。
if(corner > 0){
corner--;
for(int i = 0; i < 3; i++){ //优先找四边中没有用户棋子的地方下
if(i == 1)
continue;
boolean NoBigO = true;
for(int j = 0; j < 3; j++){
if(chessBoard[i][j] == 'O')
NoBigO = false;
}
for(int j = 0; j < 3 && NoBigO; j++){
if(chessBoard[i][j] == ' '){
chessBoard[i][j] = '*';
size++;
return;
}
}
} for(int i = 0; i < 3; i++){
for(int j = 0; j < 3; j++){
if(j == 1 || i == 1)
continue;
if(chessBoard[i][j] == ' '){
chessBoard[i][j] = '*';
size++;
return;
}
}
}
} //end if //3.判断是否可以下一个棋子,从而再下一步可以胜利(不能放在一起判断)
for(int i = 0; i < 3; i++){
for(int j = 0; j < 3; j++){
if(chessBoard[i][j] == ' '){
chessBoard[i][j] = '*';
if(willBeSuccessful(-1)){
size++;
return;
} else
chessBoard[i][j] = ' ';
}
}
} for(int i = 0; i < 3; i++){
for(int j = 0; j < 3; j++){
if(chessBoard[i][j] == ' '){
chessBoard[i][j] = 'O';
if (willBeSuccessful(1)) {
chessBoard[i][j] = '*';
size++;
return;
} else
chessBoard[i][j] = ' ';
}
}
} sillyRobot();
} //【打印棋盘】
private void printChessBroad(){
System.out.println("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); //模拟清屏
System.out.println("┌───┬───┬───┐");
System.out.println("│ " + chessBoard[0][0] + " │ " + chessBoard[0][1] + " │ " + chessBoard[0][2] + " │");
System.out.println("├───┼───┼───┤");
System.out.println("│ " + chessBoard[1][0] + " │ " + chessBoard[1][1] + " │ " + chessBoard[1][2] + " │");
System.out.println("├───┼───┼───┤");
System.out.println("│ " + chessBoard[2][0] + " │ " + chessBoard[2][1] + " │ " + chessBoard[2][2] + " │");
System.out.println("└───┴───┴───┘");
} //【判断成功逻辑】
private enum Choice{
LINE, //行
COLUMN, //列
RIGHT_DIAGONAL, //右对角线
LEFT_DIAGONAL; //左对角线
}
private int calculate(Choice choice, int i){ //计算行、列、对角线是否连成一条线
switch (choice){
case LINE:
return chessBoard[i][0] + chessBoard[i][1] + chessBoard[i][2];
case COLUMN:
return chessBoard[0][i] + chessBoard[1][i] + chessBoard[2][i];
case RIGHT_DIAGONAL:
return chessBoard[0][0] + chessBoard[1][1] + chessBoard[2][2];
case LEFT_DIAGONAL:
return chessBoard[0][2] + chessBoard[1][1] + chessBoard[2][0];
}
return 0;
}
private int isSuccessful(){
/*
返回-1系统胜;返回1用户胜;返回0表示继续下棋。
系统胜:126 == '*' + '*' + '*'
用户胜:237 == 'O' + 'O' + 'O'
*/
for(int i = 0; i < 3; i++){
if(calculate(Choice.LINE, i) == 237 || calculate(Choice.COLUMN, i) == 237)
return 1;
if(calculate(Choice.LINE, i) == 126 || calculate(Choice.COLUMN, i) == 126)
return -1;
}
if(calculate(Choice.LEFT_DIAGONAL, 0) == 237 || calculate(Choice.RIGHT_DIAGONAL, 0) == 237)
return 1;
if(calculate(Choice.LEFT_DIAGONAL, 0) == 126 || calculate(Choice.RIGHT_DIAGONAL, 0) == 126)
return -1;
return 0; //继续下棋
} private boolean willBeSuccessful(int who){ //who:-1表示判断机器人的,+1表示判断用户的。
//如果行、列、对角线有2个相同棋子的个数,则将会胜,
//190 == 2 * 'O' + ' '
//116 == 2 * '*' + ' ' int n = 0;
int s = (who == 1) ? 190 : 116; //用户or机器人要计算的值 for(int i = 0; i < 3; i++){
if(calculate(Choice.LINE, i) == s)
n++;
if(calculate(Choice.COLUMN, i) == s)
n++;
}
//因为中心一定会被占的,所以就不用判断对角线了
return n > 1;
}
}
Java实现简单井字棋的更多相关文章
- Java 小游戏 - 井字棋 v1.0 (初步完成) (2018.4.16更新)
井字棋游戏初步完成 实现功能:输入位置数据->打印棋盘->判断是否胜利->继续游戏/退出游戏 缺点:没有清屏函数 判断胜利方法太过无脑 package MYGAME; ...
- 程序设计入门—Java语言 第五周编程题 2井字棋(5分)
2 井字棋(5分) 题目内容: 嗯,就是视频里说的那个井字棋.视频里说了它的基本思路,现在,需要你把它全部实现出来啦. 你的程序先要读入一个整数n,范围是[3,100],这表示井字棋棋盘的边长.比如n ...
- Java井字棋游戏
试着写了一个井字棋游戏,希望各位能给予一些宝贵的建议. 一.棋盘类 package 井字棋游戏; public class ChessBoard { private int number; Perso ...
- quick cocos2d-x 入门---井字棋
学习quick cocos2d-x 第二天 ,使用quick-x 做了一个井字棋游戏 . 我假设读者已经 http://wiki.quick-x.com/doku.php?id=zh_cn阅读了这个链 ...
- TicTacToe井字棋 by reinforcement learning
对于初学强化学习的同学,数学公式也看不太懂, 一定希望有一些简单明了的代码实现加强对入门强化学习的直觉认识,这是一篇初级入门代码, 希望能对你们开始学习强化学习起到基本的作用. 井字棋具体玩法参考百度 ...
- 井字棋(Tic-Tac-Toe)
井字棋介绍:https://en.wikipedia.org/wiki/Tic-tac-toe 井字棋简单,但是获胜策略却和直觉不同,四角比中间重要性要高,而且先手有很大的获胜概率获胜(先手胜:91, ...
- [HTML5实现人工智能]小游戏《井字棋》发布,据说IQ上200才能赢
一,什么是TicTacToe(井字棋) 本 游戏 为在下用lufylegend开发的第二款小游戏.此游戏是大家想必大家小时候都玩过,因为玩它很简单,只需要一张草稿纸和一只笔就能开始游戏,所以广受儿 ...
- 井字棋游戏升级版 - TopTicTacToe项目 简介
一.游戏简介 井字棋是一款世界闻名的游戏,不用我说,你一定知道它的游戏规则. 这款游戏简单易学,玩起来很有意思,不过已经证明出这款游戏如果两个玩家都足够聪明的话, 是很容易无法分出胜负的,即我们得到的 ...
- 『HTML5实现人工智能』小游戏《井字棋》发布,据说IQ上200才能赢【算法&代码讲解+资源打包下载】
一,什么是TicTacToe(井字棋) 本游戏为在下用lufylegend开发的第二款小游戏.此游戏是大家想必大家小时候都玩过,因为玩它很简单,只需要一张草稿纸和一只笔就能开始游戏,所以广受儿童欢迎. ...
随机推荐
- MySQL中触发器
触发器是与某个事件相关的特殊存储过程,与存储过程不同的是,存储过程需要用 call 调用而出发器不需要使用call调用调用. 也就是自己预先定义好了,当某个事件发生时,就会自动出发触发器进行相关的操作 ...
- svn建立分支和svn代码合并的操作方法
首先说下为什么我们需要用到分支-合并.比如项目demo下有两个小组,svn下有一个trunk版.由于客户需求突然变化,导致项目需要做较大改动,此时项目组决定由小组1继续完成原来正进行到一半的工作[某个 ...
- 利用NotePad++ 格式化代码(格式标准化) worldsing
在阅读别人的代码时往往会遇到格式很乱,阅读起来很费劲,如果手动改很容易出错,而且很费时间,这时可以借助一些专业的编辑器来格式化代码,NotePad++是一个轻量级的代码编辑器,占用内存少,运行速度快, ...
- WCF技术剖析(卷1)WCF全面解析文摘
第1章 wcf简介 soa体现的是一种对关注点进行分解的思想,与技术无关 soa的基本思想: a.服务自治 独立部署,不依赖其他 b.依赖于开放的标准 采用xml,xsd,及wsdl作为服务描述的 ...
- java类的泛型DAO
@Transactional public abstract class DAOSupport<T> implements DAO<T> { protected Class&l ...
- java类加载器的一些测试
package classloader; import java.lang.reflect.Method; import org.junit.Test; import com.example.Samp ...
- LDA详解
PART 1 这个性质被叫做共轭性.共轭先验使得后验概率分布的函数形式与先验概率相同,因此使得贝叶斯分析得到了极⼤的简化. V:文档集中不重复的词汇的数目 语料库共有m篇文档,: 对于文档,由个词 ...
- Hdu1051 Wooden Sticks 2017-03-11 23:30 62人阅读 评论(0) 收藏
Wooden Sticks Problem Description There is a pile of n wooden sticks. The length and weight of each ...
- javascript与java的相互调用,纯java的javascript引擎rhino(转载)
1.下载Rhino安装包,下载地址:官网http://www.mozilla.org/rhino. 2.rhino环境配置,把解压出来的js.jar文件加入到系统的环境变量classpath 3.在命 ...
- PipelineDB On Kafka
PipelineDB 安装yum install https://s3-us-west-2.amazonaws.com/download.pipelinedb.com/pipelinedb-0.9.8 ...