需求
编写一个五子棋程序, 要有存盘退出和续上盘的功能,怎么保存棋盘数据?
tip:为了方便,不需要输入密码什么的,只要实现五子棋,存盘退出和续上盘就可以
棋盘为: 43*42
思考
思考:
1、保存和记忆功能。也就是整个程序退出之后,会把内存的数据记忆到硬盘中
* 将数据保存到一个txt中
2、初始化时:
* 路径下已经有了txt了
* txt是空的,这是第一局
* 如果txt中已经有了历史数据,从txt中读取历史数据,用来初始化内存即可
* 路径下没有有了txt了,这是第一局,那么从头开始初始化(棋盘)
* 五子棋可以用一个二维数组表示, 为了方便,暂时将数组初始化为47*44,初始时所有数据都是0
(两个第一局啊,可以综合下)
3、下棋中:
* 1表示白方,2表示黑方,保证两方交替执行(不知道别人是怎么做的,反正我保证,因此下棋时只需要告诉x, y的坐标)
* 需要一个标志来记录下一局是谁下, 初始化一个flag = 1(白字先行),当下完一个棋子之后flag=2, 在一个flag=1
* 为了方便,我这里规则x,y的(0, 0)为左上角,就是和二维数组的索引一样
* 棋子的数量是有限的, 当填满整个棋盘时退出,因此每次下完一个棋子就要判断棋盘是不是满了
4、有关退出:
退出有两种情况
* 异常退出(这个想不出来有什么情况)
* 不想下了:
当有人(关闭程序---这个没法,因为我这里是cmd型)输入字母q时表示他不想下了:
* 问他是否真的要退出
* 如果是:
* 问他是不是要保存此次的棋盘,
* 如果是,保存到txt中:
* 如果txt不存在,创造txt,并且将当前棋势写入txt
* 如果已经存在txt,清空上次的内容,并且将当前棋势写入txt(覆盖写)
* 如果不是,清空txt
* 如果不是:继续让他输入坐标
* 胜负已分: 也就是棋盘满了,这个时候(因为不懂怎么下围棋的,而且因为是轮流下的1和2的数量是一样的)就直接输出“当前局已经结束”,
然后将txt中的内容清除直接退出就好(当前局已经结束,下一句一定是新的一局)
* 如何判断棋盘是否已经满了: 二维数组元素最多只可以有row * line个,(初始化一个count),记录当前棋子数。。。。
txt中存储的内容: 二维数组,当前棋子数,下一次谁下。
用txt不方便,这里我用yaml
flag: 1
count:2,
array: ******
总结:用到的知识
* 操作文本
* 检查路径下文本是否存在
* 判断文本内容是否为空
* 从文本冲读取内容
* 覆盖写入文本
* 清空文本内容
* 解析写入yaml
* 操作二维数组
* 二维数组初始化
* 填充二维数组
准备知识
1、学习yaml
2、操作文件
- 检查路径下文本是否存在
Yaml yaml = new Yaml();//该YAML类是API的入口点:由于实现不是线程安全的,因此不同的线程必须具有自己的Yaml实例
// 加载文档
InputStream input = SnakeYaml.class.getClassLoader().getResourceAsStream("sparse.yaml"); // 不能是./src/main/resources/sparse.yaml
if (input == null){ // 如果input == null则表示resources下没有customer.yaml文件
File file = new File("./src/main/resources/sparse.yaml");
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println(input);
开始做项目
第一版
1、这是一个不使用框架的maven工程,其项目目录
2、pom.xml中引入坐标
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.oceanstar</groupId>
<artifactId>Sparse</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.23</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
3、util.java
import java.util.Scanner;
public class util {
private static Scanner scanner = new Scanner(System.in);
// 一定能得到n位数
public static String readKeyBoard(int limit) {
String line = "";
while (scanner.hasNext()) {
line = scanner.nextLine();
if (line.length() < 1 || line.length() > limit) {
System.out.print("输入长度(不大于" + limit + ")错误,请重新输入:");
continue;
}
break;
}
return line;
}
public static char readConfirmSelection(){
char c;
for (; ; ) {
String str = readKeyBoard(1);
c = str.charAt(0);
if (c != 'Y' && c != 'y' && c != 'N' && c != 'n') {
System.out.print("选择错误,请重新输入:");
} else
break;
}
return c;
}
// 直到输入正确才能跳出循环
// 参数是输入数据的范围:返回值必须在[min, max]范围内
public static int readNumber(int min, int max){
int i = 0;
for (; ; ) {
String str = readKeyBoard(2);
try {
i = Integer.parseInt(str);
if (i < min || i > max){
throw new Exception("插入范围必须在[" + min + ", " + max + "]内");
}
break;
}catch (Exception e){
System.out.print("超出坐标范围:");
}
}
return i;
}
}
4、Chessboard.java
import org.yaml.snakeyaml.Yaml;
import java.io.*;
import java.util.Arrays;
public class Chessboard {
private int flag;
private int count;
private int[][] array;
private Yaml yaml = new Yaml();
private String path = System.getProperty("user.dir") + "/src/main/resources/Chessboard.yaml";
public int getFlag() {
return flag;
}
public void setFlag(int flag) {
if (flag != 1 && flag != 2){
System.out.println("sorry, flag取值只能为1或者2");
return;
}
this.flag = flag;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public int[][] getArray() {
return array;
}
public void setArray(int[][] array) {
this.array = array;
}
@Override
public String toString() {
return "Chessboard{" +
"flag=" + flag +
", count=" + count +
", array=" + Arrays.deepToString(array) +
'}';
}
private void changeflag(){
if (flag == 1){
flag = 2;
}else{
flag = 1;
}
}
private boolean canInput(int x, int y){
return array[x][y] == 0;
}
// 下棋: 输入一定是正确的
public boolean playChess(int x, int y){
// 下棋之前检测这个位置是不是可以下
if (!canInput(x, y)){
return false;
}
// 下棋
array[x][y] = flag;
// 下棋之后:
changeflag(); // 下一次轮换
count--; // 棋子少了一个
return true;
}
// 棋盘是否满了
public boolean fullChess(){
return count == 0;
}
// 确认
public boolean isComfirm(){
char s = util.readConfirmSelection();
return s == 'Y' || s == 'y';
}
// 打印棋局
public void printChessboard(){
for (int i = 0; i < this.array.length; i++){
for (int j = 0; j < this.array[i].length; j++){
System.out.printf("%d\t", this.array[i][j]);
}
System.out.println();
}
}
// 初始化时从磁盘读取保存的棋局
public Chessboard readChessboard() {
File file =new File(path);
if (!file.exists()){
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
InputStream inputStream = null;
try {
inputStream = new FileInputStream(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
Chessboard c = yaml.loadAs(inputStream, Chessboard.class);
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
return c;
}
public void clearInfoForFile(){
File file =new File(path);
FileWriter fileWriter = null;
try {
if(!file.exists()) {
file.createNewFile();
}
fileWriter = new FileWriter(file);
fileWriter.write("");
fileWriter.flush();
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//
public void wirterChessboard(){
Chessboard c = new Chessboard();
c.setCount(this.count);
c.setFlag(this.flag);
c.setArray(this.array);
try {
FileWriter writer = new FileWriter(path);
yaml.dump(c, writer);
writer.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
5、Alphago
import org.yaml.snakeyaml.Yaml;
import java.io.*;
import java.util.Scanner;
public class Alphago {
private static Chessboard c = new Chessboard(); // 只有一个棋盘
private static int heng ;
private static int zong ;
private static void initAlphago(){
Chessboard temp = c.readChessboard(); // 读取磁盘
boolean continue_chess = false ; // 是否继续上一次的棋局
if (temp != null){
System.out.print("检测到有棋局未完成, 是否继续(Y/N), 如果否, 将开始一个新的棋局:");
continue_chess = c.isComfirm();
}
// 继续上一局
if (continue_chess){
c = temp;
}else{ // 如果读取yaml为空,压根就没有上一次
c.clearInfoForFile(); // 清除棋盘
// 重新初始化一个棋盘
c.setFlag(1); // 白字先行
c.setCount(heng*zong); // 还剩下多少步
int[][]arr = new int[heng][zong]; // 初始化棋盘
c.setArray(arr);
}
}
public static void main(String[] args) {
System.out.print("请输入棋盘横长(0, 100):");
heng = util.readNumber(0, 100);
System.out.print("请输入棋盘纵宽(0, 100):");
zong = util.readNumber(0, 100);
// 初始化棋盘
initAlphago();
// 棋盘打印
c.printChessboard();
// 开始下棋
for (; ;){
// 输入坐标
System.out.print("请输入x坐标(0, " + (heng-1) + "):");
int x = util.readNumber(0, heng-1);
System.out.print("请输入y坐标(0, " + (zong-1) + "):");
int y = util.readNumber(0, zong-1);
// 下棋
if (!c.playChess(x, y)){
System.out.println("当前位置已经有了棋子, 请另外选择位置");
continue;
}
// 打印棋盘
c.printChessboard();
// 检测是不是棋盘满了
if (c.fullChess()){
System.out.println("棋盘满了, 是否开始下一局? 如果不是,将退出本程序(Y/N)");
// 清空yaml:当前棋局结束了才清空
c.clearInfoForFile();
// 优化: 问是否要开始下一局
if (!c.isComfirm()){
break;
}
// 重新初始化一个棋盘
c.setFlag(1); // 白字先行
c.setCount(heng*zong); // 还剩下多少步
int[][]arr = new int[heng][zong]; // 初始化棋盘
c.setArray(arr);
}
//还有下一次
System.out.print("是否退出?(Y/N):");
if (!c.isComfirm()){ // 不退出则继续下一次
continue;
}
// 要退出
System.out.print("是否保存当前棋局?(Y/N):");
if (c.isComfirm()){ // 保存
c.wirterChessboard();
}
break;
}
}
}
6、使用发现如果棋盘比较大,而下的棋子比较少,将会有大量的空间被浪费
优化:因为该二维数组中很多值都是默认0,因此记录了很多没有意义得数据,因此可以使用稀疏矩阵来优化
第二版
第1,2,3步同第一版
4、Chessboard.java
import org.yaml.snakeyaml.Yaml;
import java.io.*;
import java.util.Arrays;
public class Chessboard {
private int flag; // 谁下
private int count; // 还有几步
private int[][] array; // 原始数组[下棋]
private int[][] chessarr; // 稀疏数组
/*
* row col val
* 0 heng zong 0 // 记录棋盘大小
* 1 1 2 1 // 记录非0值
* */
private Yaml yaml = new Yaml(); // 用来写
private String path = System.getProperty("user.dir") + "/src/main/resources/Chessboard.yaml";
public int[][] getChessarr() {
return chessarr;
}
public void setChessarr(int[][] chessarr) {
this.chessarr = chessarr;
}
public int getFlag() {
return flag;
}
public void setFlag(int flag) {
if (flag != 1 && flag != 2){
System.out.println("sorry, flag取值只能为1或者2");
return;
}
this.flag = flag;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public int[][] getArray() {
return array;
}
public void setArray(int[][] array) {
this.array = array;
}
@Override
public String toString() {
return "Chessboard{" +
"flag=" + flag +
", count=" + count +
", array=" + Arrays.deepToString(array) +
", chessarr=" + Arrays.deepToString(chessarr) +
'}';
}
private void changeflag(){
if (flag == 1){
flag = 2;
}else{
flag = 1;
}
}
private boolean canInput(int x, int y){
return array[x][y] == 0;
}
// 下棋: 输入一定是正确的
public boolean playChess(int x, int y){
// 下棋之前检测这个位置是不是可以下
if (!canInput(x, y)){
return false;
}
// 下棋
array[x][y] = flag;
// 下棋之后:
changeflag(); // 下一次轮换
count--; // 棋子少了一个
return true;
}
// 棋盘是否满了
public boolean fullChess(){
return count == 0;
}
// 确认
public boolean isComfirm(){
char s = util.readConfirmSelection();
return s == 'Y' || s == 'y';
}
// 打印棋局
public void printChessboard(){
for (int i = 0; i < this.array.length; i++){
for (int j = 0; j < this.array[i].length; j++){
System.out.printf("%d\t", this.array[i][j]);
}
System.out.println();
}
}
// --------------初始化时从磁盘读取保存的棋局
public Chessboard readChessboard() {
File file =new File(path);
if (!file.exists()){
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
InputStream inputStream = null;
try {
inputStream = new FileInputStream(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
Chessboard c = yaml.loadAs(inputStream, Chessboard.class);
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
return c;
}
//-----------------清除磁盘-------------------------
public void clearInfoForFile(){
File file =new File(path);
FileWriter fileWriter = null;
try {
if(!file.exists()) {
file.createNewFile();
}
fileWriter = new FileWriter(file);
fileWriter.write("");
fileWriter.flush();
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//------------------写入磁盘--------------------------------------------//
public void wirterChessboard(){
Chessboard c = new Chessboard();
c.setCount(this.count);
c.setFlag(this.flag);
// 遍历原始的二维数组,得到非0值得个数
int sum = 0;
for (int i = 0; i < array.length; i++){
for (int j = 0; j < array[i].length; j++){
if (array[i][j] != 0){
sum++;
}
}
}
//创建稀疏数组 chessarr[sum + 1][3]
chessarr = new int[sum+1][3];
// 第一行保存数组大小[至少有一行]
chessarr[0][0] = array.length;
chessarr[0][1] = array[0].length;
chessarr[0][2] = -1; // 这个值随便
int a = 1;
// 将原始数组得非0值保存到稀疏数组中
for (int i = 0; i < array.length; i++){
for (int j = 0; j < array[i].length; j++){
if (array[i][j] != 0){
chessarr[a][0] = i;
chessarr[a][1] = j;
chessarr[a][2] = array[i][j];
a++;
}
}
}
c.setChessarr(chessarr);
try {
FileWriter writer = new FileWriter(path);
yaml.dump(c, writer);
writer.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
5、Alphago.java
import org.yaml.snakeyaml.Yaml;
import java.io.*;
import java.util.Arrays;
import java.util.Scanner;
public class Alphago {
private static Chessboard c = new Chessboard(); // 只有一个棋盘
private static int heng ;
private static int zong ;
private static void initAlphago(){
Chessboard temp = c.readChessboard(); // 读取磁盘
boolean continue_chess = false ; // 是否继续上一次的棋局
if (temp != null){
System.out.print("检测到有棋局未完成, 是否继续(Y/N), 如果否, 将开始一个新的棋局:");
continue_chess = c.isComfirm();
}
c.clearInfoForFile(); // 清除棋盘
if (!continue_chess){
// 重新初始化一个棋盘
System.out.print("请输入棋盘横长(0, 100):");
heng = util.readNumber(0, 100);
System.out.print("请输入棋盘纵宽(0, 100):");
zong = util.readNumber(0, 100);
c.setFlag(1); // 白字先行
c.setCount(heng*zong); // 还剩下多少步
int[][]arr = new int[heng][zong]; // 初始化棋盘
c.setArray(arr);
}else{
c.setFlag(temp.getFlag());
c.setCount(temp.getCount());
int[][] aa = temp.getChessarr();
// aa 至少有一行
heng = aa[0][0];
zong = aa[0][1];
int[][]chess = new int[heng][zong]; // 初始化棋盘
for (int i = 1; i < aa.length; i++){
chess[aa[i][0]][aa[i][1]] = aa[i][2];
}
c.setArray(chess);
}
}
public static void main(String[] args) {
// 初始化棋盘
initAlphago();
// 棋盘打印
c.printChessboard();
// 开始下棋
for (; ;){
// 输入坐标
System.out.print("请输入x坐标(0, " + (heng-1) + "):");
int x = util.readNumber(0, heng-1);
System.out.print("请输入y坐标(0, " + (zong-1) + "):");
int y = util.readNumber(0, zong-1);
// 下棋
if (!c.playChess(x, y)){
System.out.println("当前位置已经有了棋子, 请另外选择位置");
continue;
}
// 打印棋盘
c.printChessboard();
// 检测是不是棋盘满了
if (c.fullChess()){
System.out.println("棋盘满了, 是否开始下一局? 如果不是,将退出本程序(Y/N)");
// 清空yaml:当前棋局结束了才清空
c.clearInfoForFile();
// 优化: 问是否要开始下一局
if (!c.isComfirm()){
break;
}
// 重新初始化一个棋盘
c.setFlag(1); // 白字先行
c.setCount(heng*zong); // 还剩下多少步
int[][]arr = new int[heng][zong]; // 初始化棋盘
c.setArray(arr);
}
//还有下一次
System.out.print("是否退出?(Y/N):");
if (!c.isComfirm()){ // 不退出则继续下一次
continue;
}
// 要退出
System.out.print("是否保存当前棋局?(Y/N):");
if (c.isComfirm()){ // 保存
c.wirterChessboard(); // 保存
}
break;
}
}
}
总结
基本介绍