89C51电子欧式铃

89C51电子欧式铃

一、目的及要求

基于89c51、LCD1602A以及蜂鸣器,实现带有温度显示和计数功能的电子欧式拨铃。

二、环境

  • KELI uVision4、
  • 2.STC89C51的单片机
  • 3.LCD1602A显示屏
  • 4.蜂鸣器、
  • 5.温度传感器

三、内容

编程实现按键对显示器LCD1602A的开机控制并在屏幕打印欢迎语句,同时初始化温度传感器并持续显示当前温度(每秒刷新一次)。每1.0s对K1、K2按键进行一次扫描,判断k1按键是否被按下,若K1按下则蜂鸣器发声提示有客人需要被服务且待服务数加一;判断k2键是否被按下,若有,则显示当前等待的访客数;否则一直循坏。

四、接口引脚图

  • 89C51芯片引脚接口图

  • LCD1602A引脚图

  • LCD1602A引脚功能表

  • K1、K2按键引脚图

  • 蜂鸣器

  • 温度传感器

五、原理

1.LCD1602A具体功能

DDRAM 就是显示数据 RAM,用来寄存待显示的字符代码。共 80 个字节,其地 址和屏幕的对应关系如下表:

也就是说想要在 LCD1602 屏幕的第一行第一列显示一个”A”字,就要向 DDRAM 的 00H 地址写入“A”字的代码就行了。但具体的写入是要按 LCD 模块的指令格 式来进行的,后面我会说到的。那么一行可有 40 个地址呀?是的,在 1602 中我 们就用前 16 个就行了。第二行也一样用前 16 个地址。对应如下:

刚才我说了想要在 LCD1602 屏幕的第一行第一列显示一个”A”字,就要向 DDRAM 的 00H 地址写入“A”字的代码 41H 就行了,可 41H 这一个字节的代码如 何才能让 LCD 模块在屏幕的阵点上显示“A”字呢?同样,在 LCD 模块上也固化 了字模存储器,这就是 CGROM 和 CGRAM。HD44780 内置了 192 个常用字符的字模, 存于字符产生器 CGROM(Character Generator ROM)中,另外还有 8 个允许用户 自定义的字符产生 RAM,称为 CGRAM(Character Generator RAM)。下图说明了 CGROM 和 CGRAM 与字符的对应关系。

从上图可以看出,“A”字的对应上面高位代码为 0100,对应左边低位代码 为 0001,合起来就是 01000001,也就是 41H。可见它的代码与我们 PC 中的字符 代码是基本一致的。因此我们在向DDRAM写C51字符代码程序时甚至可以直接用 P1=’A’这样的方法。PC 在编译时就把“A”先转为 41H 代码了。

2.温度传感器具体功能

DS1820 通过门开通期间内低温度系数振荡器经历的时钟周期个数计数来测量温度 而门开通 期由高温度系数振荡器决定 计数器予置对应于-55 的基数 如果在门开通期结束前计数器达到 零 那么温度寄存器 它也被予置到-55 的数值 将增量 指示温度高于-55
同时 计数器用钭率累加器电路所决定的值进行予置 为了对遵循抛物线规律的振荡器温度特 性进行补偿 这种电路是必需的 时钟再次使计数器计值至它达到零 如果门开通时间仍未结束 那么此过程再次重复
钭率累加器用于补偿振荡器温度特性的非线性 以产生高分辩率的温度测量 通过改变温度每 升高一度 计数器必须经历的计数个数来实行补偿 因此 为了获得所需的分辩率 计数器的数值
以及在给定温度处每一摄氏度的计数个数 此计算在DS1820内部完成以提供0.5 的分辩率

钭率累加器的值 二者都必须知道温度读数以 16 位读数形式提供 表 1 说明输出数据对测量温度的关系 数据在单线接口上串行发送 DS1820 可以0.5C的符号扩展的二进制补码 的增量值 在 0.5 至+125 的范围内测量温度 对于应用华氏温度的场合 必须使用查 找表或变换系数

注意 在DS1820中温度是以1/2 LSB 最低有效位 形式表示时 产生以下9位格式

以下的过程可以获得较高的分辩率 首先 读温度 并从读得的值截去0.5 位(最低有效位) 这个值便是 TEMP_READ 然后可以读留在计数器内的值 此值是门开通期停止之后计数剩余

COUNT_REMAIN 所需的最后一个数值是在该温度处每一摄氏度的计数个数 COUNT_PER_C 于 是 用户可以使用下式计算实际温度

DS1820指令集

六、代码

dooring.c

/* 下载程序后按键K1按下就会触发蜂鸣器门铃“叮咚”响  */

#include "reg52.h"
#include "LCD.h"
#include "stdlib.h"
#include "stdio.h"
#include "temp.h"

typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;

int pressflag = 0;

u8 Disp[11] = "New Guests";
u8 Disp_Hello[14] = "Welcome!!!!!";
u8 Disp_Test[9] = "Guests:";

sbit beep=P1^5;
sbit k1=P3^1;
sbit k2=P3^0;
sbit k3=P3^2;

u8 ding,dong,flag,stop;
u16 n;


u16 j=0;
u32 q=0;
u16 k;

uchar CNCHAR[7] = "Celsius";
void LcdDisplay(int);
void UsartConfiguration();


void delayl(u16 k)
{
    while(k--);
}


void delayus(u16 k)
{
       while(k--);
}


void delayms(u16 k)//
{
     for(q=0;q<k;q++)
     {
            delayus(100);
            }    
}

void delays(u16 k) //i=1?,???1s
{     
     for(q=0;q<k;q++)
     {
                delayms(1000);
     }
}

void time0init()      //定时器0初始化
{
    TMOD=0X01;     //定时器0 方式1
    TH0=0Xff;
    TL0=0X06; //定时250us
//    TR0=1;
    EA=1;
    ET0=1;
}
void biaohaoinit()       //各个标号初始化
{
    ding=0;        //叮声音  计数标志
    dong=0;        //咚声音  计数标志
    n=0;        //定时0.5s标志
    flag=0;
    stop=0;       //结束标志
}
void main()
{
    u8 i;
    u8 pressk3_flag = 0;
    char temp_now[3];

    biaohaoinit();

    LcdInit();
    LcdWriteCom(0x88);    
    LcdWriteData('C'); 

    while(1)
    {
        time0init();
        delayms(500);
        LcdInit();
        for(i=0;i<11;i++)
        {
            LcdWriteData(Disp_Hello[i]);    
        }
        LcdDisplay(Ds18b20ReadTemp());
        delays(1); //延时1秒
        if(k1==0)       //判断按键是否按下
        {
            delayl(1000);  //消抖
            if(k1==0)
            {
                pressflag = pressflag + 1;
                LcdInit();
                TR0=1;    //打开定时器0
                for(i=0;i<10;i++)
                {
                    LcdWriteData(Disp[i]);    
                }
                delays(8);
                LcdInit();
                while(!stop);
            }
        }
        if(k2==0)
        {
            delayl(1000);  //消抖
            if(k2==0)
            {
                sprintf(temp_now,"%d",pressflag);
                LcdInit();
                for(i=0;i<7;i++)
                {
                    LcdWriteData(Disp_Test[i]);    
                }
                for(i=0;i<2;i++)
                {
                    LcdWriteData(temp_now[i]);    
                }
                delays(5);
                LcdInit();
            }
        }
        if(k3==0)
        {
            pressk3_flag = pressk3_flag + 1;
            UsartConfiguration();
            delayl(1000);  //消抖
            if(k3==0)
            {
                LcdInit();
                //LcdDisplay(Ds18b20ReadTemp());
                delays(1);
            }
        }
    }    
}

void time0() interrupt 1 //定时器0中断1
{
    n++;
    TH0=0Xff;
    TL0=0X06; //250us
    if(n==2000)        //定时0.5s  叮响0.5秒,咚响0.5秒
    {
        n=0;
        if(flag==0)
        {
            flag=~flag;
        }
        else
        {
            flag=0;
            stop=1;
            TR0=0;      //关闭定时器0
        }
    }
    if(flag==0)
    {                       //通过改变定时计数时间可以改变门铃的声音
        ding++;              //叮
        if(ding==1)
        {
            ding=0;
            beep=~beep;
        }
    }
    else
    {
        dong++;
        if(dong==2)          //咚
        {
            dong=0;
            beep=~beep;
        }    
    }
}



/*******************************************************************************
* 函数名         : LcdDisplay()
* 函数功能           : LCD显示读取到的温度
* 输入           : v
* 输出              : 无
*******************************************************************************/

void LcdDisplay(int temp)      //lcd显示
{
      unsigned char i, datas[] = {0, 0, 0, 0, 0, 0, 0}; //定义数组
    float tp;  
    if(temp< 0)                //当温度值为负数
      {
          LcdWriteCom(0x80+0x40);        //写地址 80表示初始地址 +
        SBUF='-';//将接收到的数据放入到发送寄存器
        while(!TI);                     //等待发送数据完成
        TI=0;                         //清除发送完成标志位
        LcdWriteData('-');          //显示负
        //因为读取的温度是实际温度的补码,所以减1,再取反求出原码
        temp=temp-1;
        temp=~temp;
        tp=temp;
        temp=tp*0.0625*100+0.5;    
        //留两个小数点就*100,+0.5是四舍五入,因为C语言浮点数转换为整型的时候把小数点
        //后面的数自动去掉,不管是否大于0.5,而+0.5之后大于0.5的就是进1了,小于0.5的就
        //算由�0.5,还是在小数点后面。

      }
     else
      {            
          LcdWriteCom(0x80+0x40);        //写地址 80表示初始地址
        LcdWriteData('+');         //显示正
        SBUF='+';//将接收到的数据放入到发送寄存器
        while(!TI);                     //等待发送数据完成
        TI=0;                         //清除发送完成标志位
        tp=temp;//因为数据处理有小数点所以将温度赋给一个浮点型变量
        //如果温度是正的那么,那么正数的原码就是补码它本身
        temp=tp*0.0625*100+0.5;    
        //留两个小数点就*100,+0.5是四舍五入,因为C语言浮点数转换为整型的时候把小数点
        //后面的数自动去掉,不管是否大于0.5,而+0.5之后大于0.5的就是进1了,小于0.5的就
        //算加上0.5,还是在小数点后面。
    }
    datas[0] = temp / 10000;
    datas[1] = temp % 10000 / 1000;
    datas[2] = temp % 1000 / 100;
    datas[3] = temp % 100 / 10;
    datas[4] = temp % 10;
    datas[5] = 223;
    datas[6] = 'C';

    LcdWriteCom(0x82+0x40);          //写地址 80表示初始地址
    LcdWriteData('0'+datas[0]); //百位 
    SBUF = '0'+datas[0];//将接收到的数据放入到发送寄存器
    while (!TI);                     //等待发送数据完成
    TI = 0;

    LcdWriteCom(0x83+0x40);         //写地址 80表示初始地址
    LcdWriteData('0'+datas[1]); //十位
    SBUF = '0'+datas[1];//将接收到的数据放入到发送寄存器
    while (!TI);                     //等待发送数据完成
    TI = 0;

    LcdWriteCom(0x84+0x40);        //写地址 80表示初始地址
    LcdWriteData('0'+datas[2]); //个位 
    SBUF = '0'+datas[2];//将接收到的数据放入到发送寄存器
    while (!TI);                     //等待发送数据完成
    TI = 0;

    LcdWriteCom(0x85+0x40);        //写地址 80表示初始地址
    LcdWriteData('.');         //显示 ‘.’
    SBUF = '.';//将接收到的数据放入到发送寄存器
    while (!TI);                     //等待发送数据完成
    TI = 0;

    LcdWriteCom(0x86+0x40);         //写地址 80表示初始地址
    LcdWriteData('0'+datas[3]); //显示小数点  
    SBUF = '0'+datas[3];//将接收到的数据放入到发送寄存器
    while (!TI);                     //等待发送数据完成
    TI = 0;

    LcdWriteCom(0x87+0x40);         //写地址 80表示初始地址
    LcdWriteData('0'+datas[4]); //显示小数点 
    SBUF = '0'+datas[4];//将接收到的数据放入到发送寄存器
    while (!TI);                     //等待发送数据完成
    TI = 0;

    LcdWriteCom(0x88+0x40);         //写地址 80表示初始地址
    LcdWriteData(datas[5]); //显示小数点 
    SBUF = '0'+datas[5];//将接收到的数据放入到发送寄存器
    while (!TI);                     //等待发送数据完成
    TI = 0;

    LcdWriteCom(0x89+0x40);         //写地址 80表示初始地址
    LcdWriteData(datas[6]); //显示小数点 
    SBUF = '0'+datas[6];//将接收到的数据放入到发送寄存器
    while (!TI);                     //等待发送数据完成
    TI = 0;

    for(i=0; i<8; i++)
    {
         SBUF = CNCHAR[i];//将接收到的数据放入到发送寄存器
        while (!TI);                     //等待发送数据完成
        TI = 0;
    }
}
/*******************************************************************************
* 函 数 名         :UsartConfiguration()
* 函数功能           :设置串口
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/

void UsartConfiguration() 
{
    SCON=0X50;            //设置为工作方式1
    TMOD=0X20;            //设置计数器工作方式2
    PCON=0X80;            //波特率加倍
    TH1=0XF3;                //计数器初始值设置,注意波特率是4800的
    TL1=0XF3;
//    ES=1;                        //打开接收中断
//    EA=1;                        //打开总中断
    TR1=1;                    //打开计数器
}

LCD.c

#include "LCD.h"

/*******************************************************************************
* 函 数 名         : Lcd1602_Delay1ms
* 函数功能           : 延时函数,延时1ms
* 输    入         : c
* 输    出         : 无
* 说    名         : 该函数是在12MHZ晶振下,12分频单片机的延时。
*******************************************************************************/

void Lcd1602_Delay1ms(uint c)   //误差 0us
{
    uchar a,b;
    for (; c>0; c--)
    {
         for (b=199;b>0;b--)
         {
              for(a=1;a>0;a--);
         }      
    }

}

/*******************************************************************************
* 函 数 名         : LcdWriteCom
* 函数功能           : 向LCD写入一个字节的命令
* 输    入         : com
* 输    出         : 无
*******************************************************************************/
#ifndef     LCD1602_4PINS     //当没有定义这个LCD1602_4PINS时
void LcdWriteCom(uchar com)      //写入命令
{
    LCD1602_E = 0;     //使能
    LCD1602_RS = 0;       //选择发送命令
    LCD1602_RW = 0;       //选择写入

    LCD1602_DATAPINS = com;     //放入命令
    Lcd1602_Delay1ms(1);        //等待数据稳定

    LCD1602_E = 1;              //写入时序
    Lcd1602_Delay1ms(5);      //保持时间
    LCD1602_E = 0;
}
#else 
void LcdWriteCom(uchar com)      //写入命令
{
    LCD1602_E = 0;     //使能清零
    LCD1602_RS = 0;     //选择写入命令
    LCD1602_RW = 0;     //选择写入

    LCD1602_DATAPINS = com;    //由于4位的接线是接到P0口的高四位,所以传送高四位不用改
    Lcd1602_Delay1ms(1);

    LCD1602_E = 1;     //写入时序
    Lcd1602_Delay1ms(5);
    LCD1602_E = 0;

    LCD1602_DATAPINS = com << 4; //发送低四位
    Lcd1602_Delay1ms(1);

    LCD1602_E = 1;     //写入时序
    Lcd1602_Delay1ms(5);
    LCD1602_E = 0;
}
#endif
/*******************************************************************************
* 函 数 名         : LcdWriteData
* 函数功能           : 向LCD写入一个字节的数据
* 输    入         : dat
* 输    出         : 无
*******************************************************************************/           
#ifndef     LCD1602_4PINS           
void LcdWriteData(uchar dat)            //写入数据
{
    LCD1602_E = 0;    //使能清零
    LCD1602_RS = 1;    //选择输入数据
    LCD1602_RW = 0;    //选择写入

    LCD1602_DATAPINS = dat; //写入数据
    Lcd1602_Delay1ms(1);

    LCD1602_E = 1;   //写入时序
    Lcd1602_Delay1ms(5);   //保持时间
    LCD1602_E = 0;
}
#else
void LcdWriteData(uchar dat)            //写入数据
{
    LCD1602_E = 0;      //使能清零
    LCD1602_RS = 1;      //选择写入数据
    LCD1602_RW = 0;      //选择写入

    LCD1602_DATAPINS = dat;    //由于4位的接线是接到P0口的高四位,所以传送高四位不用改
    Lcd1602_Delay1ms(1);

    LCD1602_E = 1;      //写入时序
    Lcd1602_Delay1ms(5);
    LCD1602_E = 0;

    LCD1602_DATAPINS = dat << 4; //写入低四位
    Lcd1602_Delay1ms(1);

    LCD1602_E = 1;      //写入时序
    Lcd1602_Delay1ms(5);
    LCD1602_E = 0;
}
#endif
/*******************************************************************************
* 函 数 名       : LcdInit()
* 函数功能         : 初始化LCD屏
* 输    入       : 无
* 输    出       : 无
*******************************************************************************/           
#ifndef        LCD1602_4PINS
void LcdInit()                          //LCD初始化子程序
{
     LcdWriteCom(0x38);  //开显示
    LcdWriteCom(0x0c);  //开显示不显示光标
    LcdWriteCom(0x06);  //写一个指针加1
    LcdWriteCom(0x01);  //清屏
    LcdWriteCom(0x80);  //设置数据指针起点
}
#else
void LcdInit()                          //LCD初始化子程序
{
    LcdWriteCom(0x32);     //将8位总线转为4位总线
    LcdWriteCom(0x28);     //在四位线下的初始化
    LcdWriteCom(0x0c);  //开显示不显示光标
    LcdWriteCom(0x06);  //写一个指针加1
    LcdWriteCom(0x01);  //清屏
    LcdWriteCom(0x80);  //设置数据指针起点
}
#endif

LCD.h

#ifndef __LCD_H_
#define __LCD_H_
/**********************************
当使用的是4位数据传输的时候定义,
使用8位取消这个定义
**********************************/
//#define LCD1602_4PINS

/**********************************
包含头文件
**********************************/
#include<reg52.h>

//---重定义关键词---//
#ifndef uchar
#define uchar unsigned char
#endif

#ifndef uint 
#define uint unsigned int
#endif

/**********************************
PIN口定义
**********************************/
#define LCD1602_DATAPINS P0
sbit LCD1602_E=P2^7;
sbit LCD1602_RW=P2^5;
sbit LCD1602_RS=P2^6;

/**********************************
函数声明
**********************************/
/*在51单片机12MHZ时钟下的延时函数*/
void Lcd1602_Delay1ms(uint c);   //误差 0us
/*LCD1602写入8位命令子函数*/
void LcdWriteCom(uchar com);
/*LCD1602写入8位数据子函数*/    
void LcdWriteData(uchar dat)    ;
/*LCD1602初始化子程序*/        
void LcdInit();                          

#endif

temp.h

#ifndef __TEMP_H_
#define __TEMP_H_

#include "reg52.h"

sbit DSPORT=P3^7;

void Delay1ms(unsigned int );
unsigned char Ds18b20Init();
void Ds18b20WriteByte(unsigned char com);
unsigned char Ds18b20ReadByte();
void  Ds18b20ChangTemp();
void  Ds18b20ReadTempCom();
int Ds18b20ReadTemp();

#endif

temp.c

#include"temp.h"
#include "LCD.h"
/*******************************************************************************
* 函数名         : Delay1ms
* 函数功能           : 延时函数
* 输入           : 无
* 输出              : 无
*******************************************************************************/

void Delay1ms(unsigned int y)
{
    unsigned int x;
    for(y;y>0;y--)
        for(x=110;x>0;x--);
}
/*******************************************************************************
* 函数名         : Ds18b20Init
* 函数功能           : 初始化
* 输入           : 无
* 输出              : 初始化成功返回1,失败返回0
*******************************************************************************/

unsigned char Ds18b20Init()
{
    unsigned int i;
    DSPORT=0;             //将总线拉低480us~960us
    i=70;    
    while(i--);//延时642us
    DSPORT=1;            //然后拉高总线,如果DS18B20做出反应会将在15us~60us后总线拉低
    i=0;
    while(DSPORT)    //等待DS18B20拉低总线
    {
        i++;
        if(i>5000)//等待>5MS
            return 0;//初始化失败    
    }
    return 1;//初始化成功
}

/*******************************************************************************
* 函数名         : Ds18b20WriteByte
* 函数功能           : 向18B20写入一个字节
* 输入           : com
* 输出              : 无
*******************************************************************************/

void Ds18b20WriteByte(unsigned char dat)
{
    unsigned int i,j;
    for(j=0;j<8;j++)
    {
        DSPORT=0;            //每写入一位数据之前先把总线拉低1us
        i++;
        DSPORT=dat&0x01; //然后写入一个数据,从最低位开始
        i=6;
        while(i--); //延时68us,持续时间最少60us
        DSPORT=1;    //然后释放总线,至少1us给总线恢复时间才能接着写入第二个数值
        dat>>=1;
    }
}
/*******************************************************************************
* 函数名         : Ds18b20ReadByte
* 函数功能           : 读取一个字节
* 输入           : com
* 输出              : 无
*******************************************************************************/


unsigned char Ds18b20ReadByte()
{
    unsigned char byte,bi;
    unsigned int i,j;    
    for(j=8;j>0;j--)
    {
        DSPORT=0;//先将总线拉低1us
        i++;
        DSPORT=1;//然后释放总线
        i++;
        i++;//延时6us等待数据稳定
        bi=DSPORT;     //读取数据,从最低位开始读取
        /*将byte左移一位,然后与上右移7位后的bi,注意移动之后移掉那位补0。*/
        byte=(byte>>1)|(bi<<7);                          
        i=4;        //读取完之后等待48us再接着读取下一个数
        while(i--);
    }                
    return byte;
}
/*******************************************************************************
* 函数名         : Ds18b20ChangTemp
* 函数功能           : 让18b20开始转换温度
* 输入           : com
* 输出              : 无
*******************************************************************************/

void  Ds18b20ChangTemp()
{
    Ds18b20Init();
    Delay1ms(1);
    Ds18b20WriteByte(0xcc);        //跳过ROM操作命令         
    Ds18b20WriteByte(0x44);        //温度转换命令
//    Delay1ms(100);    //等待转换成功,而如果你是一直刷着的话,就不用这个延时了

}
/*******************************************************************************
* 函数名         : Ds18b20ReadTempCom
* 函数功能           : 发送读取温度命令
* 输入           : com
* 输出              : 无
*******************************************************************************/

void  Ds18b20ReadTempCom()
{    

    Ds18b20Init();
    //LcdWriteCom(0x80);
    LcdWriteCom(0xC0);   
    Delay1ms(1);
    Ds18b20WriteByte(0xcc);     //跳过ROM操作命令
    Ds18b20WriteByte(0xbe);     //发送读取温度命令
}
/*******************************************************************************
* 函数名         : Ds18b20ReadTemp
* 函数功能           : 读取温度
* 输入           : com
* 输出              : 无
*******************************************************************************/

int Ds18b20ReadTemp()
{
    int temp=0;
    unsigned char tmh,tml;
    Ds18b20ChangTemp();                 //先写入转换命令
    Ds18b20ReadTempCom();            //然后等待转换完后发送读取温度命令
    tml=Ds18b20ReadByte();        //读取温度值共16位,先读低字节
    tmh=Ds18b20ReadByte();        //再读高字节
    temp=tmh;
    temp<<=8;
    temp|=tml;
    return temp;
}

七、实物

C#的namespace和Java的package

C#的namespace和Java的package

Java中的package

在java中我们一般谈到package都知道这是java的包机,在java中为了方便类和类之间的管理我们引入了package用来解决这个问题,例如

package com.edu.test
public class A{
    public static void Test();
}

在另一个包中的另一个类中我们要想访问类A我们需要借助import关键字

import com.edu.test.A
public class B{
    public void Test2(){
        A.Test();
    }
}

当然为了访问其他包中的类我们需要public关键字,同样的为了直接调用方法我们需要关键字static声明静态方法。

Java包的作用

  • 1、把功能相似或相关的类或接口组织在同一个包中,方便类的查找和使用。
  • 2、如同文件夹一样,包也采用了树形目录的存储方式。同一个包中的类名字是不同的,不同的包中的类的名字是可以相同的,当同时调用两个不同包中相同类名的类时,应该加上包名加以区别。因此,包可以避免名字冲突。
  • 3、包也限定了访问权限,拥有包访问权限的类才能访问某个包中的类。

创建包时需要注意如下几点:

(1)创建包时用package关键字;

(2)如果有包声明,包声明一定作为源代码的第一行;

(3)包的名称一般为小写,包名要有意义。例如:数学计算包名可以命名“math”,再如,绘图包可以命名“drawing”;

C#中的namespace

声明一个namespace(命名空间)

namespace myApp
{
  public class Program
  {
    public static void Test()
  }
}

如上所示这样我们就可以在C#中声明一个命名空间

Java的package和C#的namespace的最大区别

  • namespace是可以嵌套的
namespace A
{
  namespace B
  {
    namespace C
    {

    }
  }
}

测试

  • 新建一个文件Program.cs
using System;

namespace myApp
{
    namespace Test{
        class Program2
        {
            public static void Test1()
            {
                Console.WriteLine("This is namespace Test, Class Program2, Method Test1");
            }
        }
    }
    public class Program
    {   
        public static void Test3()
        {
            Console.WriteLine("Now using namespace myApp Class Program");
        }
    }
}

如上所示我们有两个命名空间分别是myApp和Test

  • 新建一个文件Program2.cs
using myApp;
using System;

namespace myApp2
{
    class Program3
    {
        public static void Test2()
        {
            Program.Test3();
        }

        public static void Test3()
        {
            myApp.Test.Program2.Test1();
        }

        static void Main(String[] args)
        {
            Test2();
            Test3();
        }
    }
}

和java一样我们需要导入我们的命名空间这里我们使用using关键字 例如:using myApp

  • 这里新建了一个命名空间myApp2
  • 新建了两个方法Test2Test3
  • Test2方法调用myApp命名空间中的Program
  • Test3方法调用myApp命名空间的子命名空间Test中的Program1类的Test1方法

输出

已加载“/usr/local/share/dotnet/shared/Microsoft.NETCore.App/2.2.5/System.Private.CoreLib.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
已加载“/Users/rex/Desktop/Code/dotnet/myApp/bin/Debug/netcoreapp2.2/myApp.dll”。已加载符号。
已加载“/usr/local/share/dotnet/shared/Microsoft.NETCore.App/2.2.5/System.Runtime.dll”。模块已生成,不包含符号。
已加载“/usr/local/share/dotnet/shared/Microsoft.NETCore.App/2.2.5/System.Console.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
已加载“/usr/local/share/dotnet/shared/Microsoft.NETCore.App/2.2.5/System.Threading.dll”。模块已生成,不包含符号。
已加载“/usr/local/share/dotnet/shared/Microsoft.NETCore.App/2.2.5/System.Runtime.Extensions.dll”。模块已生成,不包含符号。
Now using namespace myApp Class Program
This is namespace Test, Class Program2, Method Test1
程序“[41008] myApp.dll”已退出,代码为 0 (0x0)

如上所示我们已经完成的调用

总结

  • Java的package和C#的namespace都是为了更方便的管理类
  • C#的namespace支持子namespace

人脸识别算法(Python)

人脸识别算法(Python)

写在前面

学习OpenCV 很久了,正好这段时间在学习机器学习,课程刚好讲到人脸识别

课程连接如下

TensorFlow快速入门与实战

在其中的一课中讲到了OpenCV的人脸识别算法

人脸识别的方法不止有OpenCV调用自带的训练样本集,本文提到的方法有两种

  • OpenCV人脸识别
  • face-recognition库

算法介绍(OpenCV)

这里使用到了OpenCV的一个人脸训练样本集,点击下面的超链接即可下载

haarcascade_frontalface_default.xml

这个训练集是OpenCV为我们提供的已经通过机器学习训练好的一个样本集,当然你也可以去OpenCV的GitHub主页去查看其它的训练样本集

OpenCV

为了方便日后学习,本人还是建议大家将项目clone下载可以日后查看代码。

OpenCV人脸识别算法逻辑

  • 读取图片
  • 读取样本集
  • 调用OpenCV识别人脸
  • 使用rectangle方法绘制人脸

代码实现(OpenCV)

# -*- coding: utf-8 -*-

import cv2
import sys

# Get user supplied values
imagePath = sys.argv[1]
cascPath = "haarcascade_frontalface_default.xml"

# Create the haar cascade
faceCascade = cv2.CascadeClassifier(cascPath)

# Read the image
image = cv2.imread(imagePath)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# Detect faces in image
faces = faceCascade.detectMultiScale(
    gray,
    scaleFactor=1.1,
    minNeighbors=5,
    minSize=(30, 30)
)

print("Found {0} Faces! in the Picture".format(len(faces)))

# Draw a rectangle around the faces
for(x, y, w, h) in faces:
    cv2.rectangle(image, (x,y), (x+w,y+w), (0, 255, 0), 2)

cv2.imshow("Faces found", cv2.resize(image,(1024,768)))
cv2.waitKey(0)

输出情况

处理输出情况

终端输出情况

rex@RexMacBookPro:PythonCode/FaceDetect ‹master*›$ python3 FaceDetect_Front.py /Users/rex/Desktop/乱七八糟图片/test2.jpg
Found 7 Faces! in the Picture

算法介绍(face-recognition库)

face_recognition使用世界上最简单的人脸识别工具,在Python或命令行中识别和操作人脸。使用dlib最先进的人脸识别技术构建而成,并具有深度学习功能。 该模型在Labeled Faces in the Wild基准中的准确率为99.38%。

face_recognition安装

  • pip3 install face_recognition

注意!安装之前请安装make `

  • Ubuntu sudo apt-get install make
  • Centos sudo yum install make
  • MaxOS X sudo brew install make

安装完成之后打开Python终端输入import face_recognition

若没有报错则说明安装face_recognition成功

face_recognition学习

face_recognition

face_recognition库人脸识别算逻辑

  • 读取图片
  • 使用face_recognition读取图片
  • 调用face_recognition的face_locations方法定位人脸
  • 调用OpenCV的rectangle方法绘制人脸框
  • 显示处理结果

代码实现(face_recognition库)


import cv2
import sys

import face_recognition

# Get user supplied values
imagePath = sys.argv[1]

# Load the image from face_recognition
image = face_recognition.load_image_file(imagePath)

# Detect faces in image

face_loaction = face_recognition.face_locations(image)

print("Found {0} Faces!".format(len(face_loaction)))

# Read the image with openCV

image = cv2.imread(imagePath)

# Draw a rectangle around the faces
for(top, right, bottom, left) in face_loaction:
    cv2.rectangle(image, (left, top), (right, bottom), (0, 255, 0), 2)

cv2.imshow("Faces found", cv2.resize(image,(1024,768)))
cv2.waitKey(0)

输出情况

处理输出情况

终端输出情况

rex@RexMacBookPro:PythonCode/FaceDetect ‹master*›$ python3 FaceDetect_face_recognition.py /Users/rex/Desktop/乱七八糟图片/test2.jpg 
Found 7 Faces!

End

从头开始编写一个五子棋的博弈游戏(Java)

从头开始编写一个五子棋的博弈游戏(Java)

写在前面

这几天在上人工智能,老师让我们在实验环节做一个博弈类型的游戏,这里老师提示我们可以做一个五子棋的软件,于是我尝试开始自己编写一个五子棋的Java小游戏。

设计棋盘

首先我们先绘制一个面板,我希望上面的功能有:开始游戏、悔棋、模式选择、认输的功能。

利用Swing设计面板代码如下:

public void Init(){

        JFrame jf=new JFrame();
        jf.setTitle("五子棋");
        jf.setSize(650, 580);        //先设大小,再设居中;
        jf.setLocationRelativeTo(null);
        jf.setResizable(false);
        jf.setDefaultCloseOperation(3);
        jf.setLayout(new BorderLayout());
        jf.add(this);

        JPanel eastp=new JPanel();
        eastp.setPreferredSize(new Dimension(100,0));
        JButton buttonStart=new JButton("开始游戏");
        JButton buttonregret=new JButton("悔棋");
        JButton buttonlose=new JButton("认输");
        String[] itemArray = { "人人对战", "人机对战" };
        JComboBox<String> cbItem = new JComboBox<String>(itemArray);
        buttonStart.setPreferredSize(new Dimension(90,40));
        buttonregret.setPreferredSize(new Dimension(90,40));
        buttonlose.setPreferredSize(new Dimension(90,40));
        cbItem.setPreferredSize(new Dimension(90,40));
        eastp.add(buttonStart);
        eastp.add(buttonregret);
        eastp.add(buttonlose);
        eastp.add(cbItem);
        jf.add(eastp,BorderLayout.EAST);

        jf.setVisible(true);

    }

有了面板还不够 这里我们还需要自己绘制棋盘

这里我们采用的方法是重写Swing的paint方法这样每次程序运行是都会执行paint方法

代码如下:

        // 绘制棋盘
    // 重写JPanel的paint方法
    @Override
    public void paint(Graphics g) {
        // 这里由于每次新建JPanel的时候都会调用paint方法我们这里自己重写paint方法
        super.paint(g);
        for(int i =0;i<15;i++){
            g.drawLine(30, 30 + i * 35, 30 + 35 * 14, 30 + i * 35);// 横线
            g.drawLine(30 + i * 35, 30, 30 + i * 35, 30 + 35 * 14);// 竖线
        }

    }

我们运行一个程序试试结果:

如图所示已经成功完成了面板和按钮的绘制

画出棋子

上一步我们已经可以画出棋盘,这里我们对paint方法扩充一下,可以这样来绘制棋子

//重写重绘方法,这里重写的是第一个大的JPanel的方法
    public void paint(Graphics g) {
        super.paint(g);//画出白框

        //重绘出棋盘
        g.setColor(Color.black);
        for(int i=0;i<row;i++) {
            g.drawLine(x, y+size*i, x+size*(column-1), y+size*i);
        }
        for(int j=0;j<column;j++) {
            g.drawLine(x+size*j, y, x+size*j, y+size*(row-1));
        }

        //重绘出棋子
        for(int i=0;i<row;i++) {
            for(int j=0;j<column;j++) {
                if(isAvail[i][j]==1) {
                    int countx=size*j+20;
                    int county=size*i+20;
                    g.setColor(Color.black);
                    g.fillOval(countx-size/2, county-size/2, size, size);
                }
                else if(isAvail[i][j]==2) {
                    int countx=size*j+20;
                    int county=size*i+20;
                    g.setColor(Color.white);
                    g.fillOval(countx-size/2, county-size/2, size, size);
                }
            }
        }
    }

按钮监听

已经可以成功实现一个棋盘了 我们接下来主要来编写键盘的监听方法和其他工具类
如上面所示我们在我们键盘的右边新增了几个按钮 接下来我自己又对按钮进行了一些更改如下所示:

在上面的基础上我自己重新 更改了一些按钮 添加了作者信息显示 游戏说明的按钮

实现方式

键盘的监听实现起来很简单,方法如下:

  • 实例化一个ActionEvent 对象来管理按键
  • 通过e.getActionCommand()来判断按下了哪个按钮
  • 分别实现新游戏、放弃、说明等功能

部分代码如下:


public void actionPerformed(ActionEvent e) {

        //必须得用else if,因为如果没有else if你每次在右边的界面点击时它都会获取人人对战或者人机对战的                    信息,每次都会重置棋盘数组
        //获取当前被点击按钮的内容,判断是不是"开始新游戏"这个按钮
        if(e.getActionCommand().equals("新游戏")) {
            //重绘棋盘
            for(int i=0;i<gf.isAvail.length;i++)
                for(int j=0;j<gf.isAvail[i].length;j++)
                    gf.isAvail[i][j]=0;
            gf.repaint();
            //如果是开始新游戏的按钮,再为左半部分设置监听方法
            gf.turn=1;
        }
               // forkme github关注
        else if(e.getActionCommand().equals("我的github")){
            if(java.awt.Desktop.isDesktopSupported()){
                try {
                    //创建一个URI实例
                    java.net.URI uri = java.net.URI.create("https://github.com/RoWe98");
                    //获取当前系统桌面扩展
                    java.awt.Desktop dp = java.awt.Desktop.getDesktop();
                    //判断系统桌面是否支持要执行的功能
                    if(dp.isSupported(java.awt.Desktop.Action.BROWSE)){
                    //获取系统默认浏览器打开链接
                        dp.browse(uri);
                    }
                } catch (java.io.IOException error) {
                //此为无法获取系统默认浏览器
                    error.printStackTrace();
                }
            }
        }

        // 按下readme 游戏说明
        else if(e.getActionCommand().equals("说明")){
            String readme = "1.选择一个游戏模式:人人或人机"+"\n" +
                "2.开始新游戏"+"\n"+"3.Enjoy!";
            JOptionPane.showMessageDialog(null,readme);
        }

        else if(e.getActionCommand().equals("放弃")) {
            if(gf.turn==1) {
                JOptionPane.showMessageDialog(null, "白方胜");
                System.out.println("白方胜!");
            }else JOptionPane.showMessageDialog(null, "黑方胜");
            //重新把棋盘设置为不可操作
            gf.turn=0;
        }
        else if(box.getSelectedItem().equals("人机")) {
            gf.ChooseType=1;
            gf.turn=0;
        }
        else if(box.getSelectedItem().equals("人人")){
            gf.ChooseType=0;
            gf.turn=0;
        }
    }

棋盘信息以及棋子信息存储以及设定

棋子坐标信息

首先我们来思考一个问题:如何存储当前棋子在棋盘上的坐标?

答:建立一个Bean来存储当前棋子的坐标信息

我相信大部分人都会这么想,当然我们这里也是可以这么实现这个功能的

如下所示,我们建立一个类来存放我们棋子的坐标

//新建一个棋子类ChessPosition保存每一步棋子所在的位置
public class ChessPosition {
    public int Listi,Listj;

    public ChessPosition() {

    }
    public ChessPosition(int Listi,int Listj) {
        this.Listi=Listi;
        this.Listj=Listj;
    }
}

通过这个类 就可以让我们来保存每一步棋子的位置 信息

棋盘信息设定

同样的问题,我们如何设定棋盘的信息 比如棋盘大小为15*15 ,格子大小,棋盘的起点坐标

这里我们可以使用Java中一个特别好用的东西,那就是接口,我们完全可以定义一个接口保存我们定义的信息,在之后的类中调用我们棋盘信息的时候只需要implement接口即可

接口定义如下所示:

//定义与棋盘数据相关的接口,保存棋盘的起点,格子大小,行数列数等信息
public interface GoBangconfig {
    int x=20,y=20,size=40,row=15,column=15;
}

如图我们已经设置了按键监听所以我们在主方法中为按钮添加监听

//按钮监控类
ButtonListener butListen=new ButtonListener(this,box);
//对每一个按钮都添加状态事件的监听处理机制
for(int i=0;i<butname.length;i++) {
    button[i].addActionListener(butListen);//添加发生操作的监听方法
    }

    //对可选框添加事件监听机制
    box.addActionListener(butListen);

    FrameListener fl=new FrameListener();
    fl.setGraphics(this);//获取画笔对象
    this.addMouseListener(fl);

承上启下

这一步目前是这么想的,我们准备编写具体的方法来判断棋子落子的情况

人机博弈算法来编写五子棋目前市面上主流的算法有

  • 权值法
  • α-β剪枝法
  • 博弈树法

这里我选用了权值法

权值法

权值法就是检测棋盘八个方向

  • 上、下、左、右、左上、右上、左下、右下

权值法设置了一系列权值 就像这样

情况 权值 说明
01 17 眠1连
02 12 眠1连
001 17 眠1连
002 12 眠1连
0001 17 眠1连
0002 12 眠1连
0102 17 眠1连
0201 12 眠1连
0012 15 眠1连
0021 10 眠1连
01002 19 眠1连
02001 14 眠1连
00102 17 眠1连
00201 12 眠1连
00012 15 眠1连
00021 10 眠1连
01000 21 活1连
02000 16 活1连
00100 19 活1连
00200 14 活1连
00010 17 活1连
00020 12 活1连
00001 15 活1连
00002 10 活1连
0101 65 眠2连
0202 60 眠2连
0110 65 眠2连
0220 60 眠2连
011 65 眠2连
022 60 眠2连
0011 65 眠2连
0022 60 眠2连
01012 65 眠2连
02021 60 眠2连
01102 65 眠2连
02201 60 眠2连
00112 65 眠2连
00221 60 眠2连
01010 75 活2连
02020 70 活2连
01100 75 活2连
02200 70 活2连
00110 75 活2连
00220 70 活2连
00011 75 活2连
00022 70 活2连
0111 150 眠3连
0222 140 眠3连
01112 150 眠3连
02221 140 眠3连
01101 1000 活3连
02202 800 活3连
01011 1000 活3连
02022 800 活3连
01110 1000 活3连
02220 800 活3连
01111 3000 4连
02222 3500 4连

说明

  • 0为空位
  • 1为黑棋
  • 2为白棋
  • 类似01022这种什么意思?

如图所示

  • 从左到右分别为白-黑-白
  • 且左右两边都为空
  • 所以用012表示为02120即空-白-黑-白-空

实现权值法

所以为了表示这些情况我们使用HashMap在存储对应的情况和权值

代码如下所示

public static HashMap<String,Integer> map = new HashMap<String,Integer>();//设置不同落子情况和相应权值的数组
    static {
        //被堵住
        map.put("01", 17);//眠1连
        map.put("02", 12);//眠1连
        map.put("001", 17);//眠1连
        map.put("002", 12);//眠1连
        map.put("0001", 17);//眠1连
        map.put("0002", 12);//眠1连

        map.put("0102",17);//眠1连,15
        map.put("0201",12);//眠1连,10
        map.put("0012",15);//眠1连,15
        map.put("0021",10);//眠1连,10
        map.put("01002",19);//眠1连,15
        map.put("02001",14);//眠1连,10
        map.put("00102",17);//眠1连,15
        map.put("00201",12);//眠1连,10
        map.put("00012",15);//眠1连,15
        map.put("00021",10);//眠1连,10

        map.put("01000",21);//活1连,15
        map.put("02000",16);//活1连,10
        map.put("00100",19);//活1连,15
        map.put("00200",14);//活1连,10
        map.put("00010",17);//活1连,15
        map.put("00020",12);//活1连,10
        map.put("00001",15);//活1连,15
        map.put("00002",10);//活1连,10

        //被堵住
        map.put("0101",65);//眠2连,40
        map.put("0202",60);//眠2连,30
        map.put("0110",65);//眠2连,40
        map.put("0220",60);//眠2连,30
        map.put("011",65);//眠2连,40
        map.put("022",60);//眠2连,30
        map.put("0011",65);//眠2连,40
        map.put("0022",60);//眠2连,30

        map.put("01012",65);//眠2连,40
        map.put("02021",60);//眠2连,30
        map.put("01102",65);//眠2连,40
        map.put("02201",60);//眠2连,30
        map.put("00112",65);//眠2连,40
        map.put("00221",60);//眠2连,30

        map.put("01010",75);//活2连,40
        map.put("02020",70);//活2连,30
        map.put("01100",75);//活2连,40
        map.put("02200",70);//活2连,30
        map.put("00110",75);//活2连,40
        map.put("00220",70);//活2连,30
        map.put("00011",75);//活2连,40
        map.put("00022",70);//活2连,30

        //被堵住
        map.put("0111",150);//眠3连,100
        map.put("0222",140);//眠3连,80

        map.put("01112",150);//眠3连,100
        map.put("02221",140);//眠3连,80

        map.put("01101",1000);//活3连,130
        map.put("02202",800);//活3连,110
        map.put("01011",1000);//活3连,130
        map.put("02022",800);//活3连,110
        map.put("01110", 1000);//活3连
        map.put("02220", 800);//活3连

        map.put("01111",3000);//4连,300
        map.put("02222",3500);//4连,280
    }
    public int[][] weightArray=new int[column][row];//定义一个二维数组,保存各个点的权值

如上所示我们设置不同落子情况和相应权值保存到HashMap中,并且定义一个二维数组,保存各个点的权值

其中0表示空位,1表示黑棋子,2表示白棋子。当然上面只是一个示例,实际情况比这个多得多。

权值如何给定?

  • A.随着相连的棋子数增加,它的权值要相应地增加;
  • B.相同的相连棋子数,活连的权值要比眠连要大;
  • C.人执黑,AI 执白。如果相同的相连情况,黑子的权值大于白子,那么AI就偏防守;如果黑子的权值小于白子,那么AI偏进攻。由于黑子先手,一般可以使用黑子的权值大于白子,让AI进行后手防守。(我这里用的、AI用的就是偏防守)。

细节:

  • A.比如当出现某个空位出现了4连,那么这个时候不管其他位置情况如何,我们都必须先下这个位置,因此4连的权值要比3连大得多,保证它一定能够最先被选择。
  • B.如果出现了权值相同的位置怎么办?我们这里默认选择第一个位置。

权值判断

这里我们需要编写一个方法来判断没有棋子的地方的权值,好让棋子可以根据权值来找到合适的位置来落子

当然我们需要分两个情况:

  • 人人
  • 人机
    在人人的情况下我们就不需要判断权值,人机的情况下我们把权值判断交给电脑来判断权值,从而落子

具体算法设计

  • 人下棋后先判断输赢,如果赢了则游戏结束,否则就进入AI算法

  • AI算法要做的事情就是遍历棋盘的每一个空位置,根据当前棋盘的棋子数组,来确定与该空位相关的棋子相连情况。然后和在Haspmap中进行匹配搜索,找到相应的权值,把这个权值加到当前位置。

  • 遍历权值数组,选择最大的权值,找到相应位置落子

  • 判断输赢,如果赢了则游戏结束,否则回到人下棋。

代码实现

public void mouseClicked(java.awt.event.MouseEvent e) {
        int x=e.getX();
        int y=e.getY();
        //计算棋子要落在棋盘的哪个交叉点上
        int countx=(x/40)*40+20;
        int county=(y/40)*40+20;
        Graphics g=gf.getGraphics();
        //计算棋盘上棋子在数组中相应的位置
        int Arrayj=(countx-20)/40;
        int Arrayi=(county-20)/40;

        if(gf.turn!=0)//判断是否可以进行游戏
            if(gf.isAvail[Arrayi][Arrayj]!=0) {
                System.out.println("此处已经有棋子了,请下在其它地方");
            }
            else {
                //选择人人对战
                if(gf.ChooseType==0) {
                    if(gf.turn==1) {
                        //先获取要落的地方
                        g.setColor(Color.black);
                        //落子
                        g.fillOval(countx-size/2, county-size/2, size, size);
                        //设置当前位置已经有棋子了,棋子为黑子
                        gf.isAvail[Arrayi][Arrayj]=1;
                        //把当前所下的棋子位置保存在动态数组中
                        gf.ChessPositonList.add(new ChessPosition(Arrayi,Arrayj));
                        gf.turn++;

                        //判断是否已经出现五科棋子了
                        //列判断
                        //首先界定数组范围,防止越界
                        int imin=Arrayi-4,imax=Arrayi+4;
                        if(imin<0) imin=0;
                        if(imax>14) imax=14;
                        int count1=0;//判断相连的棋子数
                        for(int i=imin;i<=imax;i++) {
                            if(gf.isAvail[i][Arrayj]==1) count1++;
                                //如果出现了其他棋子,或者是没有棋子时,就重新开始计数
                            else count1=0;
                            if(count1==5) {
                                System.out.println("黑方赢");
                                gf.PopUp("黑方赢");
                                gf.turn=0;
                                return;
                            }
                        }
                        //行判断
                        //首先界定数组范围,防止越界
                        int jmin=Arrayj-4,jmax=Arrayj+4;
                        if(jmin<0) jmin=0;
                        if(jmax>14) jmax=14;
                        int count2=0;//判断相连的棋子数
                        for(int j=jmin;j<=jmax;j++) {
                            if(gf.isAvail[Arrayi][j]==1) count2++;
                            else count2=0;
                            if(count2==5) {
                                System.out.println("黑方赢");
                                gf.PopUp("黑方赢");
                                gf.turn=0;
                                return;
                            }
                            //如果出现了其他棋子,或者是没有棋子时,就重新开始计数

                        }
                        //135度判断
                        //首先界定数组范围,防止越界
                        int count3=0;//判断相连的棋子数
                        for(int i=-4;i<=4;i++) {
                            if((Arrayi+i>=0)&&(Arrayj+i>=0)&&(Arrayi+i<=14)&&(Arrayj+i<=14)) {
                                if(gf.isAvail[Arrayi+i][Arrayj+i]==1) count3++;
                                    //如果出现了其他棋子,或者是没有棋子时,就重新开始计数
                                else count3=0;
                                if(count3==5) {
                                    System.out.println("黑方赢");
                                    gf.PopUp("黑方赢");
                                    gf.turn=0;
                                    return;
                                }
                            }
                        }
                        int count4=0;//判断相连的棋子数
                        for(int i=-4;i<=4;i++) {
                            if((Arrayi+i>=0)&&(Arrayj-i>=0)&&(Arrayi+i<=14)&&(Arrayj-i<=14)) {
                                //System.out.print("count4:"+count4);
                                if(gf.isAvail[Arrayi+i][Arrayj-i]==1) count4++;
                                    //如果出现了其他棋子,或者是没有棋子时,就重新开始计数
                                else count4=0;
                                if(count4==5) {
                                    System.out.println("黑方赢");
                                    gf.PopUp("黑方赢");
                                    gf.turn=0;
                                    return;
                                }
                            }
                        }
                    }
                    else if(gf.turn==2){
                        g.setColor(Color.white);
                        g.fillOval(countx-size/2, county-size/2, size, size);
                        //设置当前位置已经有棋子了,棋子为白子
                        gf.ChessPositonList.add(new ChessPosition(Arrayi,Arrayj));
                        gf.isAvail[Arrayi][Arrayj]=2;
                        gf.turn--;

                        //列判断
                        //首先界定数组范围,防止越界
                        int imin=Arrayi-4,imax=Arrayi+4;
                        if(imin<0) imin=0;
                        if(imax>14) imax=14;
                        int count1=0;//判断相连的棋子数
                        for(int i=imin;i<=imax;i++) {
                            if(gf.isAvail[i][Arrayj]==2) count1++;

                                //如果出现了其他棋子,或者是没有棋子时,就重新开始计数
                            else count1=0;
                            if(count1==5) {
                                System.out.println("白方赢");
                                gf.PopUp("白方赢");
                                gf.turn=0;
                                return;
                            }
                        }
                        //行判断
                        //首先界定数组范围,防止越界
                        int jmin=Arrayj-4,jmax=Arrayj+4;
                        if(jmin<0) jmin=0;
                        if(jmax>14) jmax=14;
                        int count2=0;//判断相连的棋子数
                        for(int j=jmin;j<=jmax;j++) {
                            if(gf.isAvail[Arrayi][j]==2) count2++;
                                //如果出现了其他棋子,或者是没有棋子时,就重新开始计数
                            else count2=0;
                            if(count2==5) {
                                System.out.println("白方赢");
                                gf.PopUp("白方赢");
                                gf.turn=0;
                                return;
                            }

                        }
                        //135度判断
                        //首先界定数组范围,防止越界
                        int count3=0;//判断相连的棋子数
                        for(int i=-4;i<=4;i++) {
                            if((Arrayi+i>=0)&&(Arrayj+i>=0)&&(Arrayi+i<=14)&&(Arrayj+i<=14)) {
                                if(gf.isAvail[Arrayi+i][Arrayj+i]==2) count3++;
                                    //如果出现了其他棋子,或者是没有棋子时,就重新开始计数
                                else count3=0;
                                if(count3==5) {
                                    System.out.println("白方赢");
                                    gf.PopUp("白方赢");
                                    gf.turn=0;
                                    return;
                                }
                            }
                        }
                        int count4=0;//判断相连的棋子数
                        for(int i=-4;i<=4;i++) {
                            if((Arrayi+i>=0)&&(Arrayj-i>=0)&&(Arrayi+i<=14)&&(Arrayj-i<=14)) {
                                if(gf.isAvail[Arrayi+i][Arrayj-i]==2) count4++;
                                    //如果出现了其他棋子,或者是没有棋子时,就重新开始计数
                                else count4=0;
                                if(count4==5) {
                                    System.out.println("白方赢");
                                    gf.PopUp("白方赢");
                                    gf.turn=0;
                                    return;
                                }
                            }
                        }
                    }
                }
                //如果选择的是人机对战
                else {
                    if(gf.turn==1) {

                        //人先落子
                        //先获取要落的地方
                        g.setColor(Color.black);
                        //落子
                        g.fillOval(countx-size/2, county-size/2, size, size);
                        //设置当前位置已经有棋子了,棋子为黑子
                        gf.isAvail[Arrayi][Arrayj]=1;
                        //把当前所下的棋子位置保存在动态数组中
                        gf.ChessPositonList.add(new ChessPosition(Arrayi,Arrayj));
                        gf.turn++;

                        //判断是否已经出现五科棋子了
                        //列判断
                        //首先界定数组范围,防止越界
                        int Blackimin=Arrayi-4,Blackimax=Arrayi+4;
                        if(Blackimin<0) Blackimin=0;
                        if(Blackimax>14) Blackimax=14;
                        int count1=0;//判断相连的棋子数
                        for(int i=Blackimin;i<=Blackimax;i++) {
                            if(gf.isAvail[i][Arrayj]==1) count1++;
                                //如果出现了其他棋子,或者是没有棋子时,就重新开始计数
                            else count1=0;
                            if(count1==5) {
                                System.out.println("黑方赢");
                                gf.PopUp("黑方赢");
                                return;
                            }
                        }
                        //行判断
                        //首先界定数组范围,防止越界
                        int Blackjmin=Arrayj-4,Blackjmax=Arrayj+4;
                        if(Blackjmin<0) Blackjmin=0;
                        if(Blackjmax>14) Blackjmax=14;
                        int count2=0;//判断相连的棋子数
                        for(int j=Blackjmin;j<=Blackjmax;j++) {
                            if(gf.isAvail[Arrayi][j]==1) count2++;
                            else count2=0;
                            if(count2==5) {
                                System.out.println("黑方赢");
                                gf.PopUp("黑方赢");
                                return;
                            }
                            //如果出现了其他棋子,或者是没有棋子时,就重新开始计数

                        }
                        //135度判断
                        //首先界定数组范围,防止越界
                        int count3=0;//判断相连的棋子数
                        for(int i=-4;i<=4;i++) {
                            if((Arrayi+i>=0)&&(Arrayj+i>=0)&&(Arrayi+i<=14)&&(Arrayj+i<=14)) {
                                if(gf.isAvail[Arrayi+i][Arrayj+i]==1) count3++;
                                    //如果出现了其他棋子,或者是没有棋子时,就重新开始计数
                                else count3=0;
                                if(count3==5) {
                                    System.out.println("黑方赢");
                                    gf.PopUp("黑方赢");
                                    return;
                                }
                            }
                        }
                        int count4=0;//判断相连的棋子数
                        for(int i=-4;i<=4;i++) {
                            if((Arrayi+i>=0)&&(Arrayj-i>=0)&&(Arrayi+i<=14)&&(Arrayj-i<=14)) {
                                //System.out.print("count4:"+count4);
                                if(gf.isAvail[Arrayi+i][Arrayj-i]==1) count4++;
                                    //如果出现了其他棋子,或者是没有棋子时,就重新开始计数
                                else count4=0;
                                if(count4==5) {
                                    System.out.println("黑方赢");
                                    gf.PopUp("黑方赢");
                                    return;
                                }
                            }
                        }

                        //机器落子
                        //先计算出各个位置的权值
                        for(int i=0;i<gf.isAvail.length;i++) {
                            for(int j=0;j<gf.isAvail[i].length;j++) {
                                //首先判断当前位置是否为空
                                if(gf.isAvail[i][j]==0) {
                                    //往左延伸
                                    String ConnectType="0";
                                    int jmin=Math.max(0, j-4);
                                    for(int positionj=j-1;positionj>=jmin;positionj--) {
                                        //依次加上前面的棋子
                                        ConnectType=ConnectType+gf.isAvail[i][positionj];
                                    }
                                    //从数组中取出相应的权值,加到权值数组的当前位置中
                                    Integer valueleft=gf.map.get(ConnectType);
                                    if(valueleft!=null) gf.weightArray[i][j]+=valueleft;

                                    //往右延伸
                                    ConnectType="0";
                                    int jmax=Math.min(14, j+4);
                                    for(int positionj=j+1;positionj<=jmax;positionj++) {
                                        //依次加上前面的棋子
                                        ConnectType=ConnectType+gf.isAvail[i][positionj];
                                    }
                                    //从数组中取出相应的权值,加到权值数组的当前位置中
                                    Integer valueright=gf.map.get(ConnectType);
                                    if(valueright!=null) gf.weightArray[i][j]+=valueright;

                                    //联合判断,判断行
                                    gf.weightArray[i][j]+=unionWeight(valueleft,valueright);

                                    //往上延伸
                                    ConnectType="0";
                                    int imin=Math.max(0, i-4);
                                    for(int positioni=i-1;positioni>=imin;positioni--) {
                                        //依次加上前面的棋子
                                        ConnectType=ConnectType+gf.isAvail[positioni][j];
                                    }
                                    //从数组中取出相应的权值,加到权值数组的当前位置中
                                    Integer valueup=gf.map.get(ConnectType);
                                    if(valueup!=null) gf.weightArray[i][j]+=valueup;

                                    //往下延伸
                                    ConnectType="0";
                                    int imax=Math.min(14, i+4);
                                    for(int positioni=i+1;positioni<=imax;positioni++) {
                                        //依次加上前面的棋子
                                        ConnectType=ConnectType+gf.isAvail[positioni][j];
                                    }
                                    //从数组中取出相应的权值,加到权值数组的当前位置中
                                    Integer valuedown=gf.map.get(ConnectType);
                                    if(valuedown!=null) gf.weightArray[i][j]+=valuedown;

                                    //联合判断,判断列
                                    gf.weightArray[i][j]+=unionWeight(valueup,valuedown);

                                    //往左上方延伸,i,j,都减去相同的数
                                    ConnectType="0";
                                    for(int position=-1;position>=-4;position--) {
                                        if((i+position>=0)&&(i+position<=14)&&(j+position>=0)&&(j+position<=14))
                                            ConnectType=ConnectType+gf.isAvail[i+position][j+position];
                                    }
                                    //从数组中取出相应的权值,加到权值数组的当前位置
                                    Integer valueLeftUp=gf.map.get(ConnectType);
                                    if(valueLeftUp!=null) gf.weightArray[i][j]+=valueLeftUp;

                                    //往右下方延伸,i,j,都加上相同的数
                                    ConnectType="0";
                                    for(int position=1;position<=4;position++) {
                                        if((i+position>=0)&&(i+position<=14)&&(j+position>=0)&&(j+position<=14))
                                            ConnectType=ConnectType+gf.isAvail[i+position][j+position];
                                    }
                                    //从数组中取出相应的权值,加到权值数组的当前位置
                                    Integer valueRightDown=gf.map.get(ConnectType);
                                    if(valueRightDown!=null) gf.weightArray[i][j]+=valueRightDown;

                                    //联合判断,判断行
                                    gf.weightArray[i][j]+=unionWeight(valueLeftUp,valueRightDown);

                                    //往左下方延伸,i加,j减
                                    ConnectType="0";
                                    for(int position=1;position<=4;position++) {
                                        if((i+position>=0)&&(i+position<=14)&&(j-position>=0)&&(j-position<=14))
                                            ConnectType=ConnectType+gf.isAvail[i+position][j-position];
                                    }
                                    //从数组中取出相应的权值,加到权值数组的当前位置
                                    Integer valueLeftDown=gf.map.get(ConnectType);
                                    if(valueLeftDown!=null) gf.weightArray[i][j]+=valueLeftDown;

                                    //往右上方延伸,i减,j加
                                    ConnectType="0";
                                    for(int position=1;position<=4;position++) {
                                        if((i-position>=0)&&(i-position<=14)&&(j+position>=0)&&(j+position<=14))
                                            ConnectType=ConnectType+gf.isAvail[i-position][j+position];
                                    }
                                    //从数组中取出相应的权值,加到权值数组的当前位置
                                    Integer valueRightUp=gf.map.get(ConnectType);
                                    if(valueRightUp!=null) gf.weightArray[i][j]+=valueRightUp;

                                    //联合判断,判断行
                                    gf.weightArray[i][j]+=unionWeight(valueLeftDown,valueRightUp);
                                }
                            }
                        }

                        //打印出权值
                        for(int i=0;i<go.column;i++) {
                            for(int j=0;j<go.row;j++) {
                                System.out.print(gf.weightArray[i][j]+" ");
                            }
                            System.out.println();
                        }

                        //取出最大的权值
                        int AIi=0,AIj=0;
                        int weightmax=0;
                        for(int i=0;i<go.row;i++) {
                            for(int j=0;j<go.column;j++) {
                                if(weightmax<gf.weightArray[i][j]) {
                                    weightmax=gf.weightArray[i][j];
                                    AIi=i;
                                    AIj=j;
                                    System.out.println(AIi+" "+AIj);
                                }
                            }
                        }

                        //确定位置,落子
                        g.setColor(Color.white);
                        //i对应y,j对应x
                        countx=20+AIj*40;
                        county=20+AIi*40;
                        g.fillOval(countx-size/2, county-size/2, size, size);
                        //设置当前位置已经有棋子了,棋子为白子
                        gf.ChessPositonList.add(new ChessPosition(AIi,AIj));
                        gf.isAvail[AIi][AIj]=2;
                        gf.turn--;

                        //落子以后重置权值数组weightArray
                        for(int i=0;i<go.column;i++)
                            for(int j=0;j<go.row;j++)
                                gf.weightArray[i][j]=0;

                        //列判断
                        //首先界定数组范围,防止越界
                        int imin=AIi-4,imax=AIi+4;
                        if(imin<0) imin=0;
                        if(imax>14) imax=14;
                        count1=0;//判断相连的棋子数
                        for(int i=imin;i<=imax;i++) {
                            if(gf.isAvail[i][AIj]==2) count1++;

                                //如果出现了其他棋子,或者是没有棋子时,就重新开始计数
                            else count1=0;
                            if(count1==5) {
                                System.out.println("白方赢");
                                gf.PopUp("白方赢");
                                gf.turn=0;
                                return;
                            }
                        }
                        //行判断
                        //首先界定数组范围,防止越界
                        int jmin=AIj-4,jmax=AIj+4;
                        if(jmin<0) jmin=0;
                        if(jmax>14) jmax=14;
                        count2=0;//判断相连的棋子数
                        for(int j=jmin;j<=jmax;j++) {
                            if(gf.isAvail[AIi][j]==2) count2++;
                                //如果出现了其他棋子,或者是没有棋子时,就重新开始计数
                            else count2=0;
                            if(count2==5) {
                                System.out.println("白方赢");
                                gf.PopUp("白方赢");
                                gf.turn=0;
                                return;
                            }

                        }
                        //135度判断
                        //首先界定数组范围,防止越界
                        count3=0;//判断相连的棋子数
                        for(int i=-4;i<=4;i++) {
                            if((AIi+i>=0)&&(AIj+i>=0)&&(AIi+i<=14)&&(AIj+i<=14)) {
                                if(gf.isAvail[AIi+i][AIj+i]==2) count3++;
                                    //如果出现了其他棋子,或者是没有棋子时,就重新开始计数
                                else count3=0;
                                if(count3==5) {
                                    System.out.println("白方赢");
                                    gf.PopUp("白方赢");
                                    gf.turn=0;
                                    return;
                                }
                            }
                        }
                        count4=0;//判断相连的棋子数
                        for(int i=-4;i<=4;i++) {
                            if((AIi+i>=0)&&(AIj-i>=0)&&(AIi+i<=14)&&(AIj-i<=14)) {
                                if(gf.isAvail[AIi+i][AIj-i]==2) count4++;
                                    //如果出现了其他棋子,或者是没有棋子时,就重新开始计数
                                else count4=0;
                                if(count4==5) {
                                    System.out.println("白方赢");
                                    gf.PopUp("白方赢");
                                    gf.turn=0;
                                    return;
                                }
                            }
                        }
                    }
                }
            }
    }

这里是权值AI算法的判断代码

    //AI联合算法
    public Integer unionWeight(Integer a,Integer b ) {
        //必须要先判断a,b两个数值是不是null
        if((a==null)||(b==null)) return 0;
            //一一
        else if((a>=10)&&(a<=25)&&(b>=10)&&(b<=25)) return 60;
            //一二、二一
        else if(((a>=10)&&(a<=25)&&(b>=60)&&(b<=80))||((a>=60)&&(a<=80)&&(b>=10)&&(b<=25))) return 800;
            //一三、三一、二二
        else if(((a>=10)&&(a<=25)&&(b>=140)&&(b<=1000))||((a>=140)&&(a<=1000)&&(b>=10)&&(b<=25))||((a>=60)&&(a<=80)&&(b>=60)&&(b<=80)))
            return 3000;
            //二三、三二
        else if(((a>=60)&&(a<=80)&&(b>=140)&&(b<=1000))||((a>=140)&&(a<=1000)&&(b>=60)&&(b<=80))) return 3000;
        else return 0;
    }

演示

具体演示如图所示

下载

依赖环境

  • jdk1.8

运行环境

源码

GitHub连接

转载请注明出处和作者

End

从头开始编写一个五子棋的博弈游戏(Java)(第三期)

从头开始编写一个五子棋的博弈游戏(Java)(第三期)

回顾

上一期(第二期)我们写了棋盘的按钮监听功能,以及棋盘的设置接口

写在前面

这一期目前是这么想的,我们准备编写具体的方法来判断棋子落子的情况

人机博弈算法来编写五子棋目前市面上主流的算法有

  • 权值法
  • α-β剪枝法
  • 博弈树法

这里我选用了权值法

权值法

权值法就是检测棋盘八个方向

  • 上、下、左、右、左上、右上、左下、右下

权值法设置了一系列权值 就像这样

情况 权值 说明
01 17 眠1连
02 12 眠1连
001 17 眠1连
002 12 眠1连
0001 17 眠1连
0002 12 眠1连
0102 17 眠1连
0201 12 眠1连
0012 15 眠1连
0021 10 眠1连
01002 19 眠1连
02001 14 眠1连
00102 17 眠1连
00201 12 眠1连
00012 15 眠1连
00021 10 眠1连
01000 21 活1连
02000 16 活1连
00100 19 活1连
00200 14 活1连
00010 17 活1连
00020 12 活1连
00001 15 活1连
00002 10 活1连
0101 65 眠2连
0202 60 眠2连
0110 65 眠2连
0220 60 眠2连
011 65 眠2连
022 60 眠2连
0011 65 眠2连
0022 60 眠2连
01012 65 眠2连
02021 60 眠2连
01102 65 眠2连
02201 60 眠2连
00112 65 眠2连
00221 60 眠2连
01010 75 活2连
02020 70 活2连
01100 75 活2连
02200 70 活2连
00110 75 活2连
00220 70 活2连
00011 75 活2连
00022 70 活2连
0111 150 眠3连
0222 140 眠3连
01112 150 眠3连
02221 140 眠3连
01101 1000 活3连
02202 800 活3连
01011 1000 活3连
02022 800 活3连
01110 1000 活3连
02220 800 活3连
01111 3000 4连
02222 3500 4连

说明

  • 0为空位
  • 1为黑棋
  • 2为白棋
  • 类似01022这种什么意思?

如图所示

  • 从左到右分别为白-黑-白
  • 且左右两边都为空
  • 所以用012表示为02120即空-白-黑-白-空

实现权值法

所以为了表示这些情况我们使用HashMap在存储对应的情况和权值

代码如下所示

public static HashMap<String,Integer> map = new HashMap<String,Integer>();//设置不同落子情况和相应权值的数组
    static {
        //被堵住
        map.put("01", 17);//眠1连
        map.put("02", 12);//眠1连
        map.put("001", 17);//眠1连
        map.put("002", 12);//眠1连
        map.put("0001", 17);//眠1连
        map.put("0002", 12);//眠1连

        map.put("0102",17);//眠1连,15
        map.put("0201",12);//眠1连,10
        map.put("0012",15);//眠1连,15
        map.put("0021",10);//眠1连,10
        map.put("01002",19);//眠1连,15
        map.put("02001",14);//眠1连,10
        map.put("00102",17);//眠1连,15
        map.put("00201",12);//眠1连,10
        map.put("00012",15);//眠1连,15
        map.put("00021",10);//眠1连,10

        map.put("01000",21);//活1连,15
        map.put("02000",16);//活1连,10
        map.put("00100",19);//活1连,15
        map.put("00200",14);//活1连,10
        map.put("00010",17);//活1连,15
        map.put("00020",12);//活1连,10
        map.put("00001",15);//活1连,15
        map.put("00002",10);//活1连,10

        //被堵住
        map.put("0101",65);//眠2连,40
        map.put("0202",60);//眠2连,30
        map.put("0110",65);//眠2连,40
        map.put("0220",60);//眠2连,30
        map.put("011",65);//眠2连,40
        map.put("022",60);//眠2连,30
        map.put("0011",65);//眠2连,40
        map.put("0022",60);//眠2连,30

        map.put("01012",65);//眠2连,40
        map.put("02021",60);//眠2连,30
        map.put("01102",65);//眠2连,40
        map.put("02201",60);//眠2连,30
        map.put("00112",65);//眠2连,40
        map.put("00221",60);//眠2连,30

        map.put("01010",75);//活2连,40
        map.put("02020",70);//活2连,30
        map.put("01100",75);//活2连,40
        map.put("02200",70);//活2连,30
        map.put("00110",75);//活2连,40
        map.put("00220",70);//活2连,30
        map.put("00011",75);//活2连,40
        map.put("00022",70);//活2连,30

        //被堵住
        map.put("0111",150);//眠3连,100
        map.put("0222",140);//眠3连,80

        map.put("01112",150);//眠3连,100
        map.put("02221",140);//眠3连,80

        map.put("01101",1000);//活3连,130
        map.put("02202",800);//活3连,110
        map.put("01011",1000);//活3连,130
        map.put("02022",800);//活3连,110
        map.put("01110", 1000);//活3连
        map.put("02220", 800);//活3连

        map.put("01111",3000);//4连,300
        map.put("02222",3500);//4连,280
    }
    public int[][] weightArray=new int[column][row];//定义一个二维数组,保存各个点的权值

如上所示我们设置不同落子情况和相应权值保存到HashMap中,并且定义一个二维数组,保存各个点的权值

其中0表示空位,1表示黑棋子,2表示白棋子。当然上面只是一个示例,实际情况比这个多得多。

权值如何给定?

  • A.随着相连的棋子数增加,它的权值要相应地增加;
  • B.相同的相连棋子数,活连的权值要比眠连要大;
  • C.人执黑,AI 执白。如果相同的相连情况,黑子的权值大于白子,那么AI就偏防守;如果黑子的权值小于白子,那么AI偏进攻。由于黑子先手,一般可以使用黑子的权值大于白子,让AI进行后手防守。(我这里用的、AI用的就是偏防守)。

细节:

  • A.比如当出现某个空位出现了4连,那么这个时候不管其他位置情况如何,我们都必须先下这个位置,因此4连的权值要比3连大得多,保证它一定能够最先被选择。
  • B.如果出现了权值相同的位置怎么办?我们这里默认选择第一个位置。

权值判断

这里我们需要编写一个方法来判断没有棋子的地方的权值,好让棋子可以根据权值来找到合适的位置来落子

当然我们需要分两个情况:

  • 人人
  • 人机
    在人人的情况下我们就不需要判断权值,人机的情况下我们把权值判断交给电脑来判断权值,从而落子

具体算法设计

  • 人下棋后先判断输赢,如果赢了则游戏结束,否则就进入AI算法

  • AI算法要做的事情就是遍历棋盘的每一个空位置,根据当前棋盘的棋子数组,来确定与该空位相关的棋子相连情况。然后和在Haspmap中进行匹配搜索,找到相应的权值,把这个权值加到当前位置。

  • 遍历权值数组,选择最大的权值,找到相应位置落子

  • 判断输赢,如果赢了则游戏结束,否则回到人下棋。

代码实现

public void mouseClicked(java.awt.event.MouseEvent e) {
        int x=e.getX();
        int y=e.getY();
        //计算棋子要落在棋盘的哪个交叉点上
        int countx=(x/40)*40+20;
        int county=(y/40)*40+20;
        Graphics g=gf.getGraphics();
        //计算棋盘上棋子在数组中相应的位置
        int Arrayj=(countx-20)/40;
        int Arrayi=(county-20)/40;

        if(gf.turn!=0)//判断是否可以进行游戏
            if(gf.isAvail[Arrayi][Arrayj]!=0) {
                System.out.println("此处已经有棋子了,请下在其它地方");
            }
            else {
                //选择人人对战
                if(gf.ChooseType==0) {
                    if(gf.turn==1) {
                        //先获取要落的地方
                        g.setColor(Color.black);
                        //落子
                        g.fillOval(countx-size/2, county-size/2, size, size);
                        //设置当前位置已经有棋子了,棋子为黑子
                        gf.isAvail[Arrayi][Arrayj]=1;
                        //把当前所下的棋子位置保存在动态数组中
                        gf.ChessPositonList.add(new ChessPosition(Arrayi,Arrayj));
                        gf.turn++;

                        //判断是否已经出现五科棋子了
                        //列判断
                        //首先界定数组范围,防止越界
                        int imin=Arrayi-4,imax=Arrayi+4;
                        if(imin<0) imin=0;
                        if(imax>14) imax=14;
                        int count1=0;//判断相连的棋子数
                        for(int i=imin;i<=imax;i++) {
                            if(gf.isAvail[i][Arrayj]==1) count1++;
                                //如果出现了其他棋子,或者是没有棋子时,就重新开始计数
                            else count1=0;
                            if(count1==5) {
                                System.out.println("黑方赢");
                                gf.PopUp("黑方赢");
                                gf.turn=0;
                                return;
                            }
                        }
                        //行判断
                        //首先界定数组范围,防止越界
                        int jmin=Arrayj-4,jmax=Arrayj+4;
                        if(jmin<0) jmin=0;
                        if(jmax>14) jmax=14;
                        int count2=0;//判断相连的棋子数
                        for(int j=jmin;j<=jmax;j++) {
                            if(gf.isAvail[Arrayi][j]==1) count2++;
                            else count2=0;
                            if(count2==5) {
                                System.out.println("黑方赢");
                                gf.PopUp("黑方赢");
                                gf.turn=0;
                                return;
                            }
                            //如果出现了其他棋子,或者是没有棋子时,就重新开始计数

                        }
                        //135度判断
                        //首先界定数组范围,防止越界
                        int count3=0;//判断相连的棋子数
                        for(int i=-4;i<=4;i++) {
                            if((Arrayi+i>=0)&&(Arrayj+i>=0)&&(Arrayi+i<=14)&&(Arrayj+i<=14)) {
                                if(gf.isAvail[Arrayi+i][Arrayj+i]==1) count3++;
                                    //如果出现了其他棋子,或者是没有棋子时,就重新开始计数
                                else count3=0;
                                if(count3==5) {
                                    System.out.println("黑方赢");
                                    gf.PopUp("黑方赢");
                                    gf.turn=0;
                                    return;
                                }
                            }
                        }
                        int count4=0;//判断相连的棋子数
                        for(int i=-4;i<=4;i++) {
                            if((Arrayi+i>=0)&&(Arrayj-i>=0)&&(Arrayi+i<=14)&&(Arrayj-i<=14)) {
                                //System.out.print("count4:"+count4);
                                if(gf.isAvail[Arrayi+i][Arrayj-i]==1) count4++;
                                    //如果出现了其他棋子,或者是没有棋子时,就重新开始计数
                                else count4=0;
                                if(count4==5) {
                                    System.out.println("黑方赢");
                                    gf.PopUp("黑方赢");
                                    gf.turn=0;
                                    return;
                                }
                            }
                        }
                    }
                    else if(gf.turn==2){
                        g.setColor(Color.white);
                        g.fillOval(countx-size/2, county-size/2, size, size);
                        //设置当前位置已经有棋子了,棋子为白子
                        gf.ChessPositonList.add(new ChessPosition(Arrayi,Arrayj));
                        gf.isAvail[Arrayi][Arrayj]=2;
                        gf.turn--;

                        //列判断
                        //首先界定数组范围,防止越界
                        int imin=Arrayi-4,imax=Arrayi+4;
                        if(imin<0) imin=0;
                        if(imax>14) imax=14;
                        int count1=0;//判断相连的棋子数
                        for(int i=imin;i<=imax;i++) {
                            if(gf.isAvail[i][Arrayj]==2) count1++;

                                //如果出现了其他棋子,或者是没有棋子时,就重新开始计数
                            else count1=0;
                            if(count1==5) {
                                System.out.println("白方赢");
                                gf.PopUp("白方赢");
                                gf.turn=0;
                                return;
                            }
                        }
                        //行判断
                        //首先界定数组范围,防止越界
                        int jmin=Arrayj-4,jmax=Arrayj+4;
                        if(jmin<0) jmin=0;
                        if(jmax>14) jmax=14;
                        int count2=0;//判断相连的棋子数
                        for(int j=jmin;j<=jmax;j++) {
                            if(gf.isAvail[Arrayi][j]==2) count2++;
                                //如果出现了其他棋子,或者是没有棋子时,就重新开始计数
                            else count2=0;
                            if(count2==5) {
                                System.out.println("白方赢");
                                gf.PopUp("白方赢");
                                gf.turn=0;
                                return;
                            }

                        }
                        //135度判断
                        //首先界定数组范围,防止越界
                        int count3=0;//判断相连的棋子数
                        for(int i=-4;i<=4;i++) {
                            if((Arrayi+i>=0)&&(Arrayj+i>=0)&&(Arrayi+i<=14)&&(Arrayj+i<=14)) {
                                if(gf.isAvail[Arrayi+i][Arrayj+i]==2) count3++;
                                    //如果出现了其他棋子,或者是没有棋子时,就重新开始计数
                                else count3=0;
                                if(count3==5) {
                                    System.out.println("白方赢");
                                    gf.PopUp("白方赢");
                                    gf.turn=0;
                                    return;
                                }
                            }
                        }
                        int count4=0;//判断相连的棋子数
                        for(int i=-4;i<=4;i++) {
                            if((Arrayi+i>=0)&&(Arrayj-i>=0)&&(Arrayi+i<=14)&&(Arrayj-i<=14)) {
                                if(gf.isAvail[Arrayi+i][Arrayj-i]==2) count4++;
                                    //如果出现了其他棋子,或者是没有棋子时,就重新开始计数
                                else count4=0;
                                if(count4==5) {
                                    System.out.println("白方赢");
                                    gf.PopUp("白方赢");
                                    gf.turn=0;
                                    return;
                                }
                            }
                        }
                    }
                }
                //如果选择的是人机对战
                else {
                    if(gf.turn==1) {

                        //人先落子
                        //先获取要落的地方
                        g.setColor(Color.black);
                        //落子
                        g.fillOval(countx-size/2, county-size/2, size, size);
                        //设置当前位置已经有棋子了,棋子为黑子
                        gf.isAvail[Arrayi][Arrayj]=1;
                        //把当前所下的棋子位置保存在动态数组中
                        gf.ChessPositonList.add(new ChessPosition(Arrayi,Arrayj));
                        gf.turn++;

                        //判断是否已经出现五科棋子了
                        //列判断
                        //首先界定数组范围,防止越界
                        int Blackimin=Arrayi-4,Blackimax=Arrayi+4;
                        if(Blackimin<0) Blackimin=0;
                        if(Blackimax>14) Blackimax=14;
                        int count1=0;//判断相连的棋子数
                        for(int i=Blackimin;i<=Blackimax;i++) {
                            if(gf.isAvail[i][Arrayj]==1) count1++;
                                //如果出现了其他棋子,或者是没有棋子时,就重新开始计数
                            else count1=0;
                            if(count1==5) {
                                System.out.println("黑方赢");
                                gf.PopUp("黑方赢");
                                return;
                            }
                        }
                        //行判断
                        //首先界定数组范围,防止越界
                        int Blackjmin=Arrayj-4,Blackjmax=Arrayj+4;
                        if(Blackjmin<0) Blackjmin=0;
                        if(Blackjmax>14) Blackjmax=14;
                        int count2=0;//判断相连的棋子数
                        for(int j=Blackjmin;j<=Blackjmax;j++) {
                            if(gf.isAvail[Arrayi][j]==1) count2++;
                            else count2=0;
                            if(count2==5) {
                                System.out.println("黑方赢");
                                gf.PopUp("黑方赢");
                                return;
                            }
                            //如果出现了其他棋子,或者是没有棋子时,就重新开始计数

                        }
                        //135度判断
                        //首先界定数组范围,防止越界
                        int count3=0;//判断相连的棋子数
                        for(int i=-4;i<=4;i++) {
                            if((Arrayi+i>=0)&&(Arrayj+i>=0)&&(Arrayi+i<=14)&&(Arrayj+i<=14)) {
                                if(gf.isAvail[Arrayi+i][Arrayj+i]==1) count3++;
                                    //如果出现了其他棋子,或者是没有棋子时,就重新开始计数
                                else count3=0;
                                if(count3==5) {
                                    System.out.println("黑方赢");
                                    gf.PopUp("黑方赢");
                                    return;
                                }
                            }
                        }
                        int count4=0;//判断相连的棋子数
                        for(int i=-4;i<=4;i++) {
                            if((Arrayi+i>=0)&&(Arrayj-i>=0)&&(Arrayi+i<=14)&&(Arrayj-i<=14)) {
                                //System.out.print("count4:"+count4);
                                if(gf.isAvail[Arrayi+i][Arrayj-i]==1) count4++;
                                    //如果出现了其他棋子,或者是没有棋子时,就重新开始计数
                                else count4=0;
                                if(count4==5) {
                                    System.out.println("黑方赢");
                                    gf.PopUp("黑方赢");
                                    return;
                                }
                            }
                        }

                        //机器落子
                        //先计算出各个位置的权值
                        for(int i=0;i<gf.isAvail.length;i++) {
                            for(int j=0;j<gf.isAvail[i].length;j++) {
                                //首先判断当前位置是否为空
                                if(gf.isAvail[i][j]==0) {
                                    //往左延伸
                                    String ConnectType="0";
                                    int jmin=Math.max(0, j-4);
                                    for(int positionj=j-1;positionj>=jmin;positionj--) {
                                        //依次加上前面的棋子
                                        ConnectType=ConnectType+gf.isAvail[i][positionj];
                                    }
                                    //从数组中取出相应的权值,加到权值数组的当前位置中
                                    Integer valueleft=gf.map.get(ConnectType);
                                    if(valueleft!=null) gf.weightArray[i][j]+=valueleft;

                                    //往右延伸
                                    ConnectType="0";
                                    int jmax=Math.min(14, j+4);
                                    for(int positionj=j+1;positionj<=jmax;positionj++) {
                                        //依次加上前面的棋子
                                        ConnectType=ConnectType+gf.isAvail[i][positionj];
                                    }
                                    //从数组中取出相应的权值,加到权值数组的当前位置中
                                    Integer valueright=gf.map.get(ConnectType);
                                    if(valueright!=null) gf.weightArray[i][j]+=valueright;

                                    //联合判断,判断行
                                    gf.weightArray[i][j]+=unionWeight(valueleft,valueright);

                                    //往上延伸
                                    ConnectType="0";
                                    int imin=Math.max(0, i-4);
                                    for(int positioni=i-1;positioni>=imin;positioni--) {
                                        //依次加上前面的棋子
                                        ConnectType=ConnectType+gf.isAvail[positioni][j];
                                    }
                                    //从数组中取出相应的权值,加到权值数组的当前位置中
                                    Integer valueup=gf.map.get(ConnectType);
                                    if(valueup!=null) gf.weightArray[i][j]+=valueup;

                                    //往下延伸
                                    ConnectType="0";
                                    int imax=Math.min(14, i+4);
                                    for(int positioni=i+1;positioni<=imax;positioni++) {
                                        //依次加上前面的棋子
                                        ConnectType=ConnectType+gf.isAvail[positioni][j];
                                    }
                                    //从数组中取出相应的权值,加到权值数组的当前位置中
                                    Integer valuedown=gf.map.get(ConnectType);
                                    if(valuedown!=null) gf.weightArray[i][j]+=valuedown;

                                    //联合判断,判断列
                                    gf.weightArray[i][j]+=unionWeight(valueup,valuedown);

                                    //往左上方延伸,i,j,都减去相同的数
                                    ConnectType="0";
                                    for(int position=-1;position>=-4;position--) {
                                        if((i+position>=0)&&(i+position<=14)&&(j+position>=0)&&(j+position<=14))
                                            ConnectType=ConnectType+gf.isAvail[i+position][j+position];
                                    }
                                    //从数组中取出相应的权值,加到权值数组的当前位置
                                    Integer valueLeftUp=gf.map.get(ConnectType);
                                    if(valueLeftUp!=null) gf.weightArray[i][j]+=valueLeftUp;

                                    //往右下方延伸,i,j,都加上相同的数
                                    ConnectType="0";
                                    for(int position=1;position<=4;position++) {
                                        if((i+position>=0)&&(i+position<=14)&&(j+position>=0)&&(j+position<=14))
                                            ConnectType=ConnectType+gf.isAvail[i+position][j+position];
                                    }
                                    //从数组中取出相应的权值,加到权值数组的当前位置
                                    Integer valueRightDown=gf.map.get(ConnectType);
                                    if(valueRightDown!=null) gf.weightArray[i][j]+=valueRightDown;

                                    //联合判断,判断行
                                    gf.weightArray[i][j]+=unionWeight(valueLeftUp,valueRightDown);

                                    //往左下方延伸,i加,j减
                                    ConnectType="0";
                                    for(int position=1;position<=4;position++) {
                                        if((i+position>=0)&&(i+position<=14)&&(j-position>=0)&&(j-position<=14))
                                            ConnectType=ConnectType+gf.isAvail[i+position][j-position];
                                    }
                                    //从数组中取出相应的权值,加到权值数组的当前位置
                                    Integer valueLeftDown=gf.map.get(ConnectType);
                                    if(valueLeftDown!=null) gf.weightArray[i][j]+=valueLeftDown;

                                    //往右上方延伸,i减,j加
                                    ConnectType="0";
                                    for(int position=1;position<=4;position++) {
                                        if((i-position>=0)&&(i-position<=14)&&(j+position>=0)&&(j+position<=14))
                                            ConnectType=ConnectType+gf.isAvail[i-position][j+position];
                                    }
                                    //从数组中取出相应的权值,加到权值数组的当前位置
                                    Integer valueRightUp=gf.map.get(ConnectType);
                                    if(valueRightUp!=null) gf.weightArray[i][j]+=valueRightUp;

                                    //联合判断,判断行
                                    gf.weightArray[i][j]+=unionWeight(valueLeftDown,valueRightUp);
                                }
                            }
                        }

                        //打印出权值
                        for(int i=0;i<go.column;i++) {
                            for(int j=0;j<go.row;j++) {
                                System.out.print(gf.weightArray[i][j]+" ");
                            }
                            System.out.println();
                        }

                        //取出最大的权值
                        int AIi=0,AIj=0;
                        int weightmax=0;
                        for(int i=0;i<go.row;i++) {
                            for(int j=0;j<go.column;j++) {
                                if(weightmax<gf.weightArray[i][j]) {
                                    weightmax=gf.weightArray[i][j];
                                    AIi=i;
                                    AIj=j;
                                    System.out.println(AIi+" "+AIj);
                                }
                            }
                        }

                        //确定位置,落子
                        g.setColor(Color.white);
                        //i对应y,j对应x
                        countx=20+AIj*40;
                        county=20+AIi*40;
                        g.fillOval(countx-size/2, county-size/2, size, size);
                        //设置当前位置已经有棋子了,棋子为白子
                        gf.ChessPositonList.add(new ChessPosition(AIi,AIj));
                        gf.isAvail[AIi][AIj]=2;
                        gf.turn--;

                        //落子以后重置权值数组weightArray
                        for(int i=0;i<go.column;i++)
                            for(int j=0;j<go.row;j++)
                                gf.weightArray[i][j]=0;

                        //列判断
                        //首先界定数组范围,防止越界
                        int imin=AIi-4,imax=AIi+4;
                        if(imin<0) imin=0;
                        if(imax>14) imax=14;
                        count1=0;//判断相连的棋子数
                        for(int i=imin;i<=imax;i++) {
                            if(gf.isAvail[i][AIj]==2) count1++;

                                //如果出现了其他棋子,或者是没有棋子时,就重新开始计数
                            else count1=0;
                            if(count1==5) {
                                System.out.println("白方赢");
                                gf.PopUp("白方赢");
                                gf.turn=0;
                                return;
                            }
                        }
                        //行判断
                        //首先界定数组范围,防止越界
                        int jmin=AIj-4,jmax=AIj+4;
                        if(jmin<0) jmin=0;
                        if(jmax>14) jmax=14;
                        count2=0;//判断相连的棋子数
                        for(int j=jmin;j<=jmax;j++) {
                            if(gf.isAvail[AIi][j]==2) count2++;
                                //如果出现了其他棋子,或者是没有棋子时,就重新开始计数
                            else count2=0;
                            if(count2==5) {
                                System.out.println("白方赢");
                                gf.PopUp("白方赢");
                                gf.turn=0;
                                return;
                            }

                        }
                        //135度判断
                        //首先界定数组范围,防止越界
                        count3=0;//判断相连的棋子数
                        for(int i=-4;i<=4;i++) {
                            if((AIi+i>=0)&&(AIj+i>=0)&&(AIi+i<=14)&&(AIj+i<=14)) {
                                if(gf.isAvail[AIi+i][AIj+i]==2) count3++;
                                    //如果出现了其他棋子,或者是没有棋子时,就重新开始计数
                                else count3=0;
                                if(count3==5) {
                                    System.out.println("白方赢");
                                    gf.PopUp("白方赢");
                                    gf.turn=0;
                                    return;
                                }
                            }
                        }
                        count4=0;//判断相连的棋子数
                        for(int i=-4;i<=4;i++) {
                            if((AIi+i>=0)&&(AIj-i>=0)&&(AIi+i<=14)&&(AIj-i<=14)) {
                                if(gf.isAvail[AIi+i][AIj-i]==2) count4++;
                                    //如果出现了其他棋子,或者是没有棋子时,就重新开始计数
                                else count4=0;
                                if(count4==5) {
                                    System.out.println("白方赢");
                                    gf.PopUp("白方赢");
                                    gf.turn=0;
                                    return;
                                }
                            }
                        }
                    }
                }
            }
    }

这里是权值AI算法的判断代码

    //AI联合算法
    public Integer unionWeight(Integer a,Integer b ) {
        //必须要先判断a,b两个数值是不是null
        if((a==null)||(b==null)) return 0;
            //一一
        else if((a>=10)&&(a<=25)&&(b>=10)&&(b<=25)) return 60;
            //一二、二一
        else if(((a>=10)&&(a<=25)&&(b>=60)&&(b<=80))||((a>=60)&&(a<=80)&&(b>=10)&&(b<=25))) return 800;
            //一三、三一、二二
        else if(((a>=10)&&(a<=25)&&(b>=140)&&(b<=1000))||((a>=140)&&(a<=1000)&&(b>=10)&&(b<=25))||((a>=60)&&(a<=80)&&(b>=60)&&(b<=80)))
            return 3000;
            //二三、三二
        else if(((a>=60)&&(a<=80)&&(b>=140)&&(b<=1000))||((a>=140)&&(a<=1000)&&(b>=60)&&(b<=80))) return 3000;
        else return 0;
    }

演示

具体演示如图所示

下载

依赖环境

  • jdk1.8

运行环境

源码

GitHub连接

转载请注明出处和作者

End

从头开始编写一个五子棋的博弈游戏(Java)(第二期)

从头开始编写一个五子棋的博弈游戏(Java)(第二期)

回顾

上一期)已经可以成功实现一个棋盘了 这一期我们主要来编写键盘的监听方法和其他工具类

按钮监听

如上期所示我们在我们键盘的右边新增了几个按钮 接下来我自己又对按钮进行了一些更改如下所示:

在上期的基础上我自己重新 更改了一些按钮 添加了作者信息显示 游戏说明的按钮

实现方式

键盘的监听实现起来很简单,方法如下:

  • 实例化一个ActionEvent 对象来管理按键
  • 通过e.getActionCommand()来判断按下了哪个按钮
  • 分别实现新游戏、放弃、说明等功能

部分代码如下:


public void actionPerformed(ActionEvent e) {

        //必须得用else if,因为如果没有else if你每次在右边的界面点击时它都会获取人人对战或者人机对战的                    信息,每次都会重置棋盘数组
        //获取当前被点击按钮的内容,判断是不是"开始新游戏"这个按钮
        if(e.getActionCommand().equals("新游戏")) {
            //重绘棋盘
            for(int i=0;i<gf.isAvail.length;i++)
                for(int j=0;j<gf.isAvail[i].length;j++)
                    gf.isAvail[i][j]=0;
            gf.repaint();
            //如果是开始新游戏的按钮,再为左半部分设置监听方法
            gf.turn=1;
        }
               // forkme github关注
        else if(e.getActionCommand().equals("我的github")){
            if(java.awt.Desktop.isDesktopSupported()){
                try {
                    //创建一个URI实例
                    java.net.URI uri = java.net.URI.create("https://github.com/RoWe98");
                    //获取当前系统桌面扩展
                    java.awt.Desktop dp = java.awt.Desktop.getDesktop();
                    //判断系统桌面是否支持要执行的功能
                    if(dp.isSupported(java.awt.Desktop.Action.BROWSE)){
                    //获取系统默认浏览器打开链接
                        dp.browse(uri);
                    }
                } catch (java.io.IOException error) {
                //此为无法获取系统默认浏览器
                    error.printStackTrace();
                }
            }
        }

        // 按下readme 游戏说明
        else if(e.getActionCommand().equals("说明")){
            String readme = "1.选择一个游戏模式:人人或人机"+"\n" +
                "2.开始新游戏"+"\n"+"3.Enjoy!";
            JOptionPane.showMessageDialog(null,readme);
        }

        else if(e.getActionCommand().equals("放弃")) {
            if(gf.turn==1) {
                JOptionPane.showMessageDialog(null, "白方胜");
                System.out.println("白方胜!");
            }else JOptionPane.showMessageDialog(null, "黑方胜");
            //重新把棋盘设置为不可操作
            gf.turn=0;
        }
        else if(box.getSelectedItem().equals("人机")) {
            gf.ChooseType=1;
            gf.turn=0;
        }
        else if(box.getSelectedItem().equals("人人")){
            gf.ChooseType=0;
            gf.turn=0;
        }
    }

棋盘信息以及棋子信息存储以及设定

棋子坐标信息

首先我们来思考一个问题:如何存储当前棋子在棋盘上的坐标?

答:建立一个Bean来存储当前棋子的坐标信息

我相信大部分人都会这么想,当然我们这里也是可以这么实现这个功能的

如下所示,我们建立一个类来存放我们棋子的坐标

//新建一个棋子类ChessPosition保存每一步棋子所在的位置
public class ChessPosition {
    public int Listi,Listj;

    public ChessPosition() {

    }
    public ChessPosition(int Listi,int Listj) {
        this.Listi=Listi;
        this.Listj=Listj;
    }
}

通过这个类 就可以让我们来保存每一步棋子的位置 信息

棋盘信息设定

同样的问题,我们如何设定棋盘的信息 比如棋盘大小为15*15 ,格子大小,棋盘的起点坐标

这里我们可以使用Java中一个特别好用的东西,那就是接口,我们完全可以定义一个接口保存我们定义的信息,在之后的类中调用我们棋盘信息的时候只需要implement接口即可

接口定义如下所示:

//定义与棋盘数据相关的接口,保存棋盘的起点,格子大小,行数列数等信息
public interface GoBangconfig {
    int x=20,y=20,size=40,row=15,column=15;
}

如图我们已经设置了按键监听所以我们在主方法中为按钮添加监听

//按钮监控类
ButtonListener butListen=new ButtonListener(this,box);
//对每一个按钮都添加状态事件的监听处理机制
for(int i=0;i<butname.length;i++) {
    button[i].addActionListener(butListen);//添加发生操作的监听方法
    }

    //对可选框添加事件监听机制
    box.addActionListener(butListen);

    FrameListener fl=new FrameListener();
    fl.setGraphics(this);//获取画笔对象
    this.addMouseListener(fl);

本期主要写按钮监听和棋盘棋子设定

End

为黑苹果解锁AirDrop功能和蓝牙以及WI-FI

为黑苹果解锁AirDrop功能和蓝牙以及WI-FI

写在前面

笔者的黑苹果安装在ThinkPad T450上由于网卡是因特尔的网卡 在黑苹果下面不支持WiFi和蓝牙,于是就想重新安装个网卡,网卡如图所示:

网卡为NGFF接口的

解决方案

在网上搜索之后发现博通的网卡可以免驱 最巧的是macbook 自带的网卡就是博通的网卡,于是我在某宝上购买了一款型号为BCM94360CS2的博通网卡

这款网卡正好佩戴一个转接卡可以让我们安装到主板的网卡接口上

安装

打开笔记本的后盖找到安装网卡的位置 拆下我们之前的intel的网卡 安装上我们博通的网卡,然后插好天线

安装完成就像这样

测试

由于我们安装的macbook原装的网卡,所以按道理上我们的网卡在黑苹果上面蓝牙和WIFi都是免驱的

如图所示 我们电脑的WiFi 蓝牙 都可以正常使用

当然最重要的是AirDrop 完美使用!!

Finder界面

接受手机给电脑的AirDrop

手机给电脑发送

End

解决黑苹果Usb配件需要电源

解决黑苹果Usb配件需要电源

问题描述

这几天在使用u盘和移动硬盘的时候突然发现会出现一个问题

如图所示:

在远景论坛和tonymacx86上面搜寻了半天大概已经确定了问题

USB3.0没有完美驱动成功

问题解决

众所周知安装黑苹果的过程中最难的一部分不是安装而是配置驱动,这让我想着如何是好,于是我就去GitHub上找到我当时下载EFI文件 的那个仓库中 仔细阅读了他的README文档,其中在这样一个文件夹里面找到了一个文件

Lenovo-T450-USB.kext

他在README里面是这么描述的

Miscellaneous

  • A collection of different kernel extensions that I could use with this build but are currently not in use. I keep them in this folder for quick access if I find my self in a situation where I need them so that I do not have to download them all over again. The most important kext in this directory is the USB-T450-Injector.kext file. It can be used to inject the systems proper USB configuration in the absence of a properly configured USBInjectall + .aml configuration file. Don’t get rid of it. If your USB ports ever stop working just load this kext into the “Other” folder and reboot.

  • 意思是如果你使用USBinjectall+.aml文件有问题的话 可以用USB-T450-Injector.kext文件代替

使用方法很简单

  • 打开terminal输入diskutil list
  • 找到EFI分区 使用sudo diskutil mount [EFI分区盘名] 挂载EFI分区
  • 在EFI分区的CLOVER文件夹中的kext中的 Other中 删除之前的USBInjectall.kext替换为Lenovo-T450-USB.kext

  • 重启计算机即可,在我重启之后发现已经解决了问题

如图所示

如图所示速度也是USB3.0的速度,这时候就说明了我们的USB3.0已经完美驱动了

解除USB端口数量限制补丁

使用方法

  • 打开Clover Configurator 在这下载
  • 然后点击挂载分区输入密码后选择EFI文件 如图所示

  • 然后在内核和驱动补丁这一块点击四次加号按照名字加入如下内容
Comment: **USB** port limit patch #1 10.14.x modify by DalianSky(credit ydeng)

Name: com.apple.iokit.IO**USB**HostFamily

Find: 83FB0F0F

Replace: 83FB3F0F

MatchOS: 10.14.x



Comment: **USB** port limit patch #2 10.14.x modify by DalianSky(credit PMHeart)

Name: com.apple.iokit.IO**USB**HostFamily

Find: 83E30FD3

Replace: 83E33FD3

MatchOS: 10.14.x



Comment: **USB** Port limit patch #3 10.14.x modify by DalianSky(credits PMheart)

Name: com.apple.driver.usb.Apple**USB**XHCI

Find: 83FB0F0F

Replace: 83FB3F0F

MatchOS: 10.14.x



Comment: **USB** Port limit patch #4 10.14.x modify by DalianSky(credits PMheart)

Name: com.apple.driver.usb.Apple**USB**XHCI

Find: 83FF0F0F

Replace: 83FF3F0F

MatchOS: 10.14.x
  • 如图所示

  • 然后command+s 保存关闭即可
  • 重启计算机即可解决问题

END

黑苹果安装教程 (Mojave 10.14.5) ThinkPad T450

黑苹果安装教程 (Mojave 10.14.5)

记一次我的黑苹果安装教程,本教程适用于ThinkPad T450 傻瓜式教学

准备工作

准备工具

  • U盘大于8G
  • U盘大小不限做PE盘
  • etcher
  • EFI文件(每个机型不同)
  • 系统镜像

下载镜像

macOS Mojave 10.14.5 18F132 正式版 with Clover 4928原版镜像

制作安装镜像

  • select image 选择镜像
  • select drive 选择u盘
  • flash 写入u盘

将提前准备好的EFI复制粘贴到USB安装盘的EFI分区下

显示磁盘分区信息

打开终端输入:diskutil list

RexdeMacBook-Pro:~ rex$ diskutil list
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                        +8.0 GB     disk4
   1:                        EFI EFI                     209.7 MB   disk4s1
   2:                  Apple_HFS Install macOS Mojave         7.7 GB     disk4s2

/dev/disk3 (disk image):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                        +7.0 GB     disk3
   1:                        EFI EFI                     209.7 MB   disk3s1
   2:                  Apple_HFS Install macOS Mojave    6.7 GB     disk3s2

根据磁盘空间查看是哪个分区 找到 EFI字样 的u盘 和 我们打开的镜像

分别为 插入的u盘flash后的分区 GUID_partition_scheme +8.0 GB disk4

其efi分区为EFI EFI 209.7 MB disk4s1

同理我们提前打开的dmg镜像的EFI为disk3s1

分别挂载这两个EFI

sudo diskutil mount disk4s1
sudo diskutil mount disk3s1

这里如果你想用傻瓜安装方法的话把你打开的镜像里面的EFI分区文件复制粘贴到USB的EFI分区里

把BOOT 和CLOVER 复制粘贴到USB的EFI分区里放在EFI文件夹里

!!!!注意

这里的EFI是万能EFI是黑果小兵大大编写的万能EFI不保证可以使用

如果你想使用现成EFI 完美支持T450的EFI那么进入如下链接下载EFI替换EFI

这里的EFI是由jsassu20为我们调试好的EFI直接就可以替换使用!

BIOS 设定

  • 关闭Bios的安全启动
  • 设置启动模式为EFI

安装MacOS

  • 开机按F12 进入启动项选择选择U盘启动
  • 进入Clover主菜单
  • 选择Boot OS X Install from install macOS Mojave
  • 开始引导macOS系统

这个过程需要1-2分钟,耐心等待进入安装程序,出现语言选择界面

  • 语言选择 选择简体中文

出现macos实用工具 界面,选择磁盘工具

磁盘工具

选择显示所有设备

选择SSD Media,点击抹掉按钮,选择默认的Mac OS扩展(日志型),将名称修改为Macintosh HD,点击抹掉按钮

假设您的磁盘是空的且数据是已经备份过的,别怪我没提醒你!!!

抹盘成功后,它会自动生成一个200MB的EFI分区,这样做的好处是不会出现安装过程中的由于EFI分区尺寸小于200MB而引起的无法安装的错误.当然前提是你的磁盘中没有重要的数据,或者您的数据已经提前备份过了.

到这里,磁盘工具的动作就已经结束了.退出磁盘工具进入安装界面,进行系统的安装了.

安装

  • 进入安装界面
  • 继续
  • 同意
  • 选择Macintosh HD 安装

期间,它会把USB安装盘上的安装文件预复制到要安装的系统分区里,数据复制完后,它会自动重启

然后进行第二阶段的安装

安装第二阶段

第二阶段的安装会有两种界面,一种是不进安装界面直接安装,另一种是先进入安装界面直接安装,需要注意的是,无论是哪一种界面下,安装的过程中全程是禁用鼠标和键盘的,需要你做的只是耐心等待它安装完成

安装速度取决于你的磁盘速度,第二阶段的安装已经与USB安装盘没什么关系了.macOS 10.14的安装完全区别于10.13,它不能免二次安装,甚至还需要重启多次,这些都是正常现象,请不要大惊小怪的.
系统安装完成后,重启进入系统设置向导

设置向导

这里不需要多讲,自己设置等待完成就行。

将USB引导更换为磁盘引导

这个时候我默认认为你已经可以进入系统了

####由于我们之前启动一直使用的USB引导 我们也不可能永远插着U盘,所以这里我们需要把U盘上的EFI复制到系统盘 Macintosh HD

  • 打开终端输入diskutil list
  • 和之前把系统的EFI复制到U盘步骤一样找到你的系统盘的EFI分区和U盘的EFI分区分别挂载将U盘的EFI复制到硬盘中即可
  • 如图所示

如果不出什么意外你用的是刚才在github上下载的EFI的话这个时候你应该是驱动都完美的

附上我的桌面电池、Wi-Fi、触摸板、声卡、网卡都是完美驱动

如果安装有问题,驱动有问题查看 黑果小兵的教程

问题解决

1.USB3.0提示插入电源

删除EFI文件夹CLOVER文件夹KEXTS目录下的Other文件夹里面USBinject.kext

2.没有我的机型的EFI

进入这个页面查找你的机型 机型列表

3.找不到镜像下载地址

进入页面镜像下载

写在最后

参考文档

从头开始编写一个五子棋的博弈游戏(Java)(第一期)

从头开始编写一个五子棋的博弈游戏(Java)(第一期)

写在前面

这几天在上人工智能,老师让我们在实验环节做一个博弈类型的游戏,这里老师提示我们可以做一个五子棋的软件,于是我尝试开始自己编写一个五子棋的Java小游戏。

设计棋盘

首先我们先绘制一个面板,我希望上面的功能有:开始游戏、悔棋、模式选择、认输的功能。

利用Swing设计面板代码如下:

public void Init(){

        JFrame jf=new JFrame();
        jf.setTitle("五子棋");
        jf.setSize(650, 580);        //先设大小,再设居中;
        jf.setLocationRelativeTo(null);
        jf.setResizable(false);
        jf.setDefaultCloseOperation(3);
        jf.setLayout(new BorderLayout());
        jf.add(this);

        JPanel eastp=new JPanel();
        eastp.setPreferredSize(new Dimension(100,0));
        JButton buttonStart=new JButton("开始游戏");
        JButton buttonregret=new JButton("悔棋");
        JButton buttonlose=new JButton("认输");
        String[] itemArray = { "人人对战", "人机对战" };
        JComboBox<String> cbItem = new JComboBox<String>(itemArray);
        buttonStart.setPreferredSize(new Dimension(90,40));
        buttonregret.setPreferredSize(new Dimension(90,40));
        buttonlose.setPreferredSize(new Dimension(90,40));
        cbItem.setPreferredSize(new Dimension(90,40));
        eastp.add(buttonStart);
        eastp.add(buttonregret);
        eastp.add(buttonlose);
        eastp.add(cbItem);
        jf.add(eastp,BorderLayout.EAST);

        jf.setVisible(true);

    }

有了面板还不够 这里我们还需要自己绘制棋盘

这里我们采用的方法是重写Swing的paint方法这样每次程序运行是都会执行paint方法

代码如下:

        // 绘制棋盘
    // 重写JPanel的paint方法
    @Override
    public void paint(Graphics g) {
        // 这里由于每次新建JPanel的时候都会调用paint方法我们这里自己重写paint方法
        super.paint(g);
        for(int i =0;i<15;i++){
            g.drawLine(30, 30 + i * 35, 30 + 35 * 14, 30 + i * 35);// 横线
            g.drawLine(30 + i * 35, 30, 30 + i * 35, 30 + 35 * 14);// 竖线
        }

    }

我们运行一个程序试试结果:

如图所示已经成功完成了面板和按钮的绘制

画出棋子

上一步我们已经可以画出棋盘,这里我们对paint方法扩充一下,可以这样来绘制棋子

//重写重绘方法,这里重写的是第一个大的JPanel的方法
    public void paint(Graphics g) {
        super.paint(g);//画出白框

        //重绘出棋盘
        g.setColor(Color.black);
        for(int i=0;i<row;i++) {
            g.drawLine(x, y+size*i, x+size*(column-1), y+size*i);
        }
        for(int j=0;j<column;j++) {
            g.drawLine(x+size*j, y, x+size*j, y+size*(row-1));
        }

        //重绘出棋子
        for(int i=0;i<row;i++) {
            for(int j=0;j<column;j++) {
                if(isAvail[i][j]==1) {
                    int countx=size*j+20;
                    int county=size*i+20;
                    g.setColor(Color.black);
                    g.fillOval(countx-size/2, county-size/2, size, size);
                }
                else if(isAvail[i][j]==2) {
                    int countx=size*j+20;
                    int county=size*i+20;
                    g.setColor(Color.white);
                    g.fillOval(countx-size/2, county-size/2, size, size);
                }
            }
        }
    }