1. 程式人生 > >數獨小遊戲

數獨小遊戲

背景 farm mod OS stock ttr tps 功能 不同

寫了個小遊戲,畫面非常簡陋,但基本的功能實現了
下面是學習WindowsAPI時參考的文章,雖說幾乎沒看懂。。。
[https://blog.csdn.net/farmwang/article/details/50603608]

[https://blog.csdn.net/celte/article/details/10243309]

[https://blog.csdn.net/m0_37801033/article/details/71540942]

單獨繪制數獨邊框時:(我對顏色搭配實在沒啥心得。。。還湊活吧)

技術分享圖片

單獨繪制數獨界面時:

技術分享圖片

main.cpp:

#include <iostream>
#include "soduko.h"
using namespace std;
int main()
{
    system("title 數獨");
    system("mode con cols=80 lines=35");
    soduko matrixone("sodukosamples");
    matrixone.play();
    return 0;
}

soduko.h:

#ifndef SODUKO_H_INCLUDED
#define SODUKO_H_INCLUDED
#include <iostream>
#include <windows.h>
#include <cstring>
#include <string>
#include <fstream>
#include <sstream>
#include <conio.h>
using namespace std;
//程序中的數字如果可能造成歧義,盡可能用變量名代替
const int line_basement=12,col_basement=30,region_intverl=2,conorcolor=7,errcolor=4,norcolor=3;
const int ONC=800,SBC=240;
const int HIGH_BITSIZE=16,WEIGHT_BITSIZE=8;
class soduko
{
public:
    soduko(string);
    ~soduko();
    void play();//主體函數
    void finderrws(int,int);//尋找錯誤,並為錯誤設置狀態
    void relieverr();//當錯誤被修正時,解除錯誤狀態
    bool finderr(int [9][9],int,int);//判斷是否存在錯誤,因為功能與性質的不同不能和finderrws函數合並,感覺有點別扭
    void show(int,int,int);//在當前所在的位置顯示
    void showsame(int,int,int);//當前不是空格時,顯示所有與當前數字相同的數字
    void solvanswer(int [9][9]);//計算數獨答案
private:
    int sodukodata[9][9];
    int statepara[9][9][4];//position x , position y , color , if can be changed //
};

#endif // SODUKO_H_INCLUDED

soduko.cpp:

#include "soduko.h"


//problem : cannot use "show_same" to relieve the block with SBC//has been overcome

/***********************************外部函數區(WindowsAPI)***************************************/
HANDLE hout=GetStdHandle(STD_OUTPUT_HANDLE);//獲取當前窗口句柄(也就是獲取窗口狀態)
COORD position;
void hide()//隱藏光標
{
    CONSOLE_CURSOR_INFO cursor_info={1,0};
    SetConsoleCursorInfo(hout, &cursor_info);
}
void SetCurPos(const int x, const int y)//設置光標位置
{
    position.X = x;
    position.Y = y;
    SetConsoleCursorPosition(hout, position);
}
void SetColor(int colorID)//設置文本顏色
{
    SetConsoleTextAttribute(hout, colorID);
}
void SetBackColor()//設置文本背景色
{
    SetConsoleTextAttribute(hout,
                            //BACKGROUND_BLUE |
                            BACKGROUND_INTENSITY|
                            BACKGROUND_GREEN |
                            BACKGROUND_RED );
}
//下面兩個函數原諒我現學現賣,另外,它們沒有也不應該出現在程序的正常線程中,因為一些問題尚未解決
void music()
{
    //播放音樂,需要在鏈接器中添加"winmm",支持wav格式,其他格式似乎不支持
    //如果文件不在工程/項目目錄下,添加確切位置
    //還有,播放音樂時不能進行其他操作,所以不能加入遊戲中(除非有暫停和繼續播放音樂的函數。。。)
    PlaySound("Baptism.wav",NULL,SND_SYNC);
}
//繪制數獨的邊框,但是只學得懵懵懂懂,單獨列出還行,作為遊戲的一部分就慘不忍睹
//另外,需要在鏈接器中添加"gdi32",因為調用了GDI圖形接口
//還有,我在編譯這個函數時(單獨的項目),發現總是畫不出來,還得碰運氣。。。
//作為經驗之談,編譯出現"undefined reference to ......"之類錯誤的,都是沒有在鏈接器中添加相應的鏈接
//最後,這個函數還沒有正式移入這個程序中,所以沒有優化邏輯和細節,造成數字較多,比較臃腫
void drawframe()
{
    HPEN hPen;//定義畫筆
    HBRUSH hBrush;//定義畫刷
    HWND wnd=FindWindow(NULL,"數獨");//不知道獲得當前窗口設備句柄的函數,還好找到了這個,獲得窗口名為“數獨”的窗口的設備句柄
    HDC dc=GetDC(wnd);//這個。。。真不懂,大概是獲取什麽東西
    POINT ipion[]={{256,192}};//起始點,不怎麽懂
    int posx[]={256,256,272,272,304,304,320,320,352,352,368,368,
                240,384,384,240,240,384,384,240,240,384,384,240};
    int posy[]={192,336,336,192,192,336,336,192,192,336,336,192,
                208,208,224,224,256,256,272,272,304,304,320,320};
    int ppx[]={288,288,336,336,240,384,384,246};
    int ppy[]={192,336,336,192,240,240,288,288};
    //繪制矩形
    hPen = (HPEN)(GetStockObject(NULL_PEN));
    hPen = CreatePen(PS_SOLID, 5, RGB(128, 128, 128));
    SelectObject(dc, hPen);
    Rectangle(dc,237,189,388,340);
    DeleteObject(hPen);//釋放畫筆
    //連線(細線)//之所以沒有用矩形來代替連線,是因為函數繪制的矩形實際上不是由線組成,而是一個面,所以多重矩形並不能組成網格
    hPen = CreatePen(PS_SOLID, 3, RGB(128, 128, 128));
    SelectObject(dc, hPen);
    MoveToEx(dc,ppx[0],ppy[0],ipion);
    for(int i=1;i<4;i++)
        LineTo(dc,ppx[i],ppy[i]);
    MoveToEx(dc,ppx[4],ppy[4],ipion);
    for(int i=5;i<8;i++)
        LineTo(dc,ppx[i],ppy[i]);
    DeleteObject(hPen);
    //連線(粗線)
    hPen = CreatePen(PS_DOT, 1, RGB(128, 128, 128));
    SelectObject(dc, hPen);
    hBrush = (HBRUSH)(GetStockObject(BLACK_BRUSH));
    SelectObject(dc,hBrush);
    MoveToEx(dc,posx[0],posy[0],ipion);
    for(int i=1;i<12;i++)
        LineTo(dc, posx[i], posy[i]);
    MoveToEx(dc,posx[12],posy[12],ipion);
    for(int i=13;i<24;i++)
        LineTo(dc, posx[i], posy[i]);
    DeleteObject(hPen);
}
/************************************************************************************/
soduko::soduko(string textdata)
{
    //數獨的數據存儲在文件中
    //格式為:文件中數據與數獨的顯示相似,數據為整型,用空格分開
    //數獨未填寫空格用0表示,其余依舊為1~9
    int a=textdata.size();
    string judgeprinciple=textdata.substr(a-4,4);
    if(judgeprinciple!=".txt")textdata+=".txt";
    ifstream input(textdata);
    if(!input)
        cout<<"Error! Cannot open the file!"<<endl;
    string s;
    for(int i=0;i<9;i++)
    {
        getline(input,s);
        istringstream scin(s);
        for(int j=0;j<9;j++)
            scin>>sodukodata[i][j];
    }
    for(int i=0;i<9;i++)
    {
        for(int j=0;j<9;j++)
        {
            //設置狀態(位置,顏色,是否可變)
            statepara[i][j][0]=j*region_intverl+col_basement;//position x
            statepara[i][j][1]=i+line_basement;//position y
            if(sodukodata[i][j]==0)
            {
                statepara[i][j][2]=norcolor;//normal color = blue
                statepara[i][j][3]=1;//can be changed
            }
            else
            {
                statepara[i][j][2]=conorcolor;//normal color = white
                statepara[i][j][3]=0;//can not be changed
            }
        }
    }
}
soduko::~soduko(){};
void soduko::play()
{
    //calculate answer//采取的計算方法一點也不高明,但勝在簡單(按照人的正常思路計算比較復雜)
    int answer[9][9];
    for(int i=0;i<9;i++)
        for(int j=0;j<9;j++)
            answer[i][j]=sodukodata[i][j];
    solvanswer(answer);

    //draw the begin view

    //drawframe();
    //music();

    for(int i=0;i<9;i++)
        for(int j=0;j<9;j++)
            show(i,j,ONC);
    //original position of choose
    show(0,0,SBC);
    showsame(0,0,SBC);
    SetCurPos(statepara[0][0][0],statepara[0][0][1]);
    hide();
    //main
    int active_x=0,active_y=0;
    char numget;
    while((numget=getch()))
    {
        showsame(active_x,active_y,ONC);
        if(numget>48&&numget<58&&statepara[active_x][active_y][3]!=0)
        {
            sodukodata[active_x][active_y]=numget-48;
            show(active_x,active_y,SBC);
        }
        else if(numget==8&&statepara[active_x][active_y][3]!=0)
        {
            sodukodata[active_x][active_y]=0;
            show(active_x,active_y,SBC);
        }
        else
        {
            show(active_x,active_y,ONC);
            switch(numget)
            {
                case 72:
                    if(active_x>0)
                        active_x--;
                    break;//up//明明是UP為什麽是x變化呢?這個問題交給讀者了
                case 75:
                    if(active_y>0)
                        active_y--;
                    break;//left
                case 80:
                    if(active_x<8)
                        active_x++;
                    break;//down
                case 77:
                    if(active_y<8)
                        active_y++;
                    break;//right
                default:break;
            }
        }
        hide();
        showsame(active_x,active_y,SBC);
        relieverr();
        finderrws(active_x,active_y);
        show(active_x,active_y,SBC);
        //relocate active position
        SetColor(statepara[active_x][active_y][2]);
        SetCurPos(statepara[active_x][active_y][0],statepara[active_x][active_y][1]);
        //judge win
        int win=1;
        for(int i=0;i<9;i++)
            {
                for(int j=0;j<9;j++)
                    if(sodukodata[i][j]!=answer[i][j])
                    {
                        win=0;
                        break;
                    }
                if(win==0)break;
            }
        if(win==1)
        {
            SetCurPos(col_basement,3);
            SetColor(conorcolor);
            cout<<"congratulation! You win the game!"<<endl;
            break;
        }
    }
}
void soduko::finderrws(int line,int ver)
{
    //find out same number and show them
    int linp=line/3*3,verp=ver/3*3;
    for(int i=0;i<9;i++)
    {
        if(i!=ver&&sodukodata[line][i]!=0)
            if(sodukodata[line][ver]==sodukodata[line][i])
            {
                show(line,ver,errcolor);
                show(line,i,errcolor);
            }
        if(i!=line&&sodukodata[i][ver]!=0)
            if(sodukodata[line][ver]==sodukodata[i][ver])
            {
                show(line,ver,errcolor);
                show(i,ver,errcolor);
            }
    }
    for(int i=linp;i<linp+3;i++)
        for(int j=verp;j<verp+3;j++)
            if((i!=line||j!=ver)&&sodukodata[i][j]!=0)
                if(sodukodata[line][ver]==sodukodata[i][j])
                {
                    show(line,ver,errcolor);
                    show(i,j,errcolor);
                }
}
bool soduko::finderr(int data[9][9],int line,int ver)
{
    int linp=line/3*3,verp=ver/3*3;
    for(int i=0;i<9;i++)
    {
        if(i!=ver&&data[line][i]!=0)
            if(data[line][ver]==data[line][i])
                return false;
        if(i!=line&&data[i][ver]!=0)
            if(data[line][ver]==data[i][ver])
                return false;
    }
    for(int i=linp;i<linp+3;i++)
        for(int j=verp;j<verp+3;j++)
            if((i!=line||j!=ver)&&sodukodata[i][j]!=0)
                if(data[line][ver]==data[i][j])
                    return false;
    return true;
}
void soduko::relieverr()
{
    for(int i=0;i<9;i++)
        for(int j=0;j<9;j++)
            if(statepara[i][j][2]==errcolor&&finderr(sodukodata,i,j))
            {
                if(statepara[i][j][3]==1)
                    show(i,j,norcolor);
                else
                    show(i,j,conorcolor);
            }
}
void soduko::show(int xt,int yt,int color)
{
    if(color==SBC)
        SetBackColor();
    else
    {
        if(color!=ONC)
            statepara[xt][yt][2]=color;
        SetColor(statepara[xt][yt][2]);
    }
    SetCurPos(statepara[xt][yt][0],statepara[xt][yt][1]);
    if(sodukodata[xt][yt]!=0)
        cout<<sodukodata[xt][yt]<<" ";//說實話,不論加不加這個空格,都感覺有些別扭
    else
        cout<<"  ";//這是兩個空格
}
void soduko::showsame(int x,int y,int order)
{
    if(sodukodata[x][y]!=0)
    {
        for(int i=0;i<9;i++)
            for(int j=0;j<9;j++)
                if(sodukodata[i][j]==sodukodata[x][y]&&i!=x&&j!=y)
                {
                    if(order!=SBC)
                        order=statepara[i][j][2];
                    show(i,j,order);
                }
    }
}
//這個函數解數獨的方法是:建立指針數組,將所有空格的指針按序存入數組
//用指針數組按序試數並記錄當前所試的數,沒有發現錯誤時試下一個
//當所有數字填入都出現錯誤時,抹去當前數字的記錄,返回上一個,繼續從上一次記錄開始試數
//按這種方法一定能找到唯一解,只要數獨有解
void soduko::solvanswer(int soduko[9][9])
{
    int numl=0,actnuml=0;
    for(int i=0;i<9;i++)
        for(int j=0;j<9;j++)
        if(soduko[i][j]==0)numl++;
    int *emptyspace[numl];
    int poslines[numl],posvert[numl];
    for(int i=0;i<9;i++)
        for(int j=0;j<9;j++)
            if(soduko[i][j]==0)
            {
                poslines[actnuml]=i;
                posvert[actnuml]=j;
                emptyspace[actnuml++]=&soduko[i][j];
            }
    actnuml=0;
    while(actnuml<numl)
    {
        int pl=0,drt=*emptyspace[actnuml];
        for(int i=drt+1;i<=9;i++)
        {
            *emptyspace[actnuml]=i;
            if(finderr(soduko,poslines[actnuml],posvert[actnuml]))
            {
                pl=1;
                break;
            }
        }
        if(pl==1)
            actnuml++;
        else
        {
            *emptyspace[actnuml]=0;
            actnuml--;
        }
    }
}

數獨小遊戲