//■ナンプレパズルの自動生成とゲームもどき // @左クリックで場所を選ぶ // A右に並んでいる数字のボタンをクリックして数字を選ぶ #include "myWin.h" #include "stdlib.h" #include "math.h" #include "time.h" #define BT_1 3001 // ボタン1 #define BT_2 3002 // ボタン2 #define BT_3 3003 // ボタン3 #define BT_4 3004 // ボタン4 #define BT_5 3005 // ボタン5 #define BT_6 3006 // ボタン6 #define BT_7 3007 // ボタン7 #define BT_8 3008 // ボタン8 #define BT_9 3009 // ボタン9 #define CSIZE 42 #define frand() ((double)rand()/(RAND_MAX+1)) static int IX, IY; static LOGFONT lfF; // 表示用フォント void fontIni(){ // フォント初期設定 lfF.lfHeight = 32; lfF.lfWidth = lfF.lfEscapement = lfF.lfOrientation = 0; lfF.lfWeight = FW_BOLD; lfF.lfItalic = lfF.lfUnderline = lfF.lfStrikeOut = FALSE; lfF.lfCharSet = SHIFTJIS_CHARSET; lfF.lfOutPrecision = OUT_DEFAULT_PRECIS; lfF.lfClipPrecision = CLIP_DEFAULT_PRECIS; lfF.lfQuality = DEFAULT_QUALITY; lfF.lfPitchAndFamily = 0; lfF.lfFaceName[0] = '\0'; } static HWND bt1, bt2, bt3, bt4, bt5, bt6, bt7, bt8, bt9; // 実行指定用ボタン int tb[9][9],ansTb[9][9],Q[9][9], user[9][9]; int multiCheck=0;int numAns=0; int rand9(){return (int)(((double)rand()/(RAND_MAX+1))*9+1);}/*1から9の乱数*/ int checkRand9(int ist,int jst, int R){ /*同じ数字がブロック中にないかを判定*/ for(int i=0; i<3;i ++) for(int j=0;j<3; j++)if(tb[i+ist][j+ist]==R) return R; return 0; } void setRand9(int ist,int jst){/*ブロックに数字を設定*/ for(int i=0; i<3;i ++)for(int j=0;j<3; j++){ int R; do R=rand9(); while(checkRand9(ist,ist, R)!=0); tb[ist+i][jst+j]=R; } } void initTb(){/*判定用テーブルにブロック単位の数字を設定*/ for(int i=0;i<9;i++ ) for(int j=0;j<9;j++) tb[i][j]=ansTb[i][j]=Q[i][j]=user[i][j]=0; for(int i=0;i<9;i+=3) setRand9(i,i); } int numPre(); /*空白マスの試行関数のプロトタイプ宣言*/ int checkNumPre(int ix, int iy){ /*ナンプレ条件判定関数*/ int kx=(ix / 3) * 3, ky=(iy / 3)*3, x=tb[ix][iy]; for(int i=0; i<3;i++) for(int j=0; j<3; j++) if(((kx+i)!=ix ||(ky+j) !=iy) && (tb[kx+i][ky+j] == x)) return 0; for(int i=0; i<9; i++) if((ix !=i) && (x == tb[i] [iy])) return 0; for(int i=0; i<9; i++) if((iy !=i) && (x == tb[ix][i] )) return 0; return numPre(); } int numPre(){ /*空白マスの試行関数*/ for(int i=0; i<9; i++)for(int j=0; j<9; j++){ if(tb[i][j]==0){ for(int k=1; k<10; k++){ tb[i][j]=k;if(checkNumPre(i,j)) return 1; } tb[i][j]=0; return 0; } } numAns++; /* 複数解チェックかつ解の個数 = 1 */ if(multiCheck && numAns==1) return 0; /* のとき,再試行 ( 失敗とみなす) */ return 1; /* 単一解 / 個数=2のとき終了(成功) */ } void copyAnsTb(){/* 判定用領域を解答領域に移す*/ for(int i=0;i<9;i++)for(int j=0;j<9;j++) ansTb[i][j]=tb[i][j]; } void copyfromQ(){/* 問題用領域を判定用領域に移す*/ for(int i=0;i<9;i++)for(int j=0;j<9;j++) tb[i][j]=Q[i][j]; } void clearQ(){ /* 問題用領域のクリア*/ for(int i=0;i<9;i++)for(int j=0; j<9; j++) tb[i][j]=0; } void genQ(int N){ /* ランダムな位置のN個の値を解答領域から問題領域に移す */ int ix, iy; /* これが問題生成となる */ for(int k=0; k < N; k++){ do{ ix = rand9(); iy = rand9();} while(Q[ix-1][iy-1] !=0); Q[ix-1][iy-1]=ansTb[ix-1][iy-1]; } } int makeTb(int NumRest){ initTb(); multiCheck=0;numAns=0;int R=numPre(); int N = NumRest; copyAnsTb(); clearQ(); do{ /*生成した問題に複数解があれば再生成*/ genQ(N); copyfromQ();multiCheck=1;numAns=0; int R=numPre(); if(numAns==1) return 0; N=2; } while(1); return 0; } int notEnd(){/*ゲーム終了でないかを判定*/ for(int i=0;i<9;i++)for(int j=0;j<9;j++) { if(Q[i][j]==0 && user[i][j]==0) return 1; } return 0; } void DrawCell(HDC hdc){//ナンプレの現在の状況を表示する HBRUSH hbrush=CreateSolidBrush(0x00FFFF); HBRUSH hbrush2=CreateSolidBrush(0xFFFFFF); HPEN hpen=CreatePen(PS_SOLID,1,0x0055FF); SelectObject(hdc,hpen);//マス目の表示 for(int i=0, X=10;i<9;i++, X+=CSIZE)for(int j=0,Y=10; j<9; j++, Y+=CSIZE){ if(Q[j][i]!=0) SelectObject(hdc,hbrush); else SelectObject(hdc,hbrush2); Rectangle(hdc, X,Y, X+CSIZE, Y+CSIZE); } DeleteObject(hbrush2); if(IX>=0){//選択マスの表示 hbrush2=CreateSolidBrush(0xFF7777); SelectObject(hdc,hbrush2); int X=10+IX*CSIZE, Y=10+IY*CSIZE; Rectangle(hdc, X,Y, X+CSIZE, Y+CSIZE); DeleteObject(hbrush2); } DeleteObject(hpen);//ブロック区切りの表示 hpen=CreatePen(PS_SOLID,4,0x0055FF);SelectObject(hdc,hpen); for(int i=0, X=10;i<=9;i+=3, X+= CSIZE*3){ MoveToEx(hdc,X,10,NULL);LineTo(hdc,X,10+CSIZE*9); } for(int j=0,Y=10; j<=9; j+=3, Y+=CSIZE*3){ MoveToEx(hdc,10, Y,NULL);LineTo(hdc,10+CSIZE*9,Y); } TCHAR str[][3]= {TEXT(" "),TEXT("1"),TEXT("2"),TEXT("3"),TEXT("4"), TEXT("5"),TEXT("6"),TEXT("7"),TEXT("8"),TEXT("9")}; HFONT hf=CreateFontIndirect(&lfF);//問題の数字表示 SetTextColor(hdc,0xFF);SetBkMode(hdc, TRANSPARENT); SelectObject(hdc,hf); for(int i=0, X=10;i<9;i++, X+=CSIZE)for(int j=0,Y=10; j<9; j++, Y+=CSIZE) TextOut(hdc, X+8,Y+4,str[Q[j][i]],1); SetTextColor(hdc,0x00);//ユーザが指定した数字の表示 for(int i=0, X=10;i<9;i++, X+=CSIZE)for(int j=0,Y=10; j<9; j++, Y+=CSIZE) if(user[j][i]!=0) TextOut(hdc, X+8,Y+4,str[user[j][i]],1); DeleteObject(hbrush);DeleteObject(hpen); } HWND createButton(HWND hw, LPARAM lp,HMENU hm, int X, int Y, TCHAR str[]){//ボタン生成 return CreateWindow(TEXT("BUTTON"),str, WS_CHILD|WS_VISIBLE|WS_BORDER|BS_PUSHBUTTON, X,Y,30,30,hw,hm,hInstance, NULL); } void procCreate(HWND hw, WPARAM wp, LPARAM lp){ SetWindowText(hw,TEXT("ナンプレパズルの自動生成とゲームもどき")); MoveWindow(hw,0,0,450,450,TRUE); bt1 =createButton(hw,lp,(HMENU) BT_1,400, 20+CSIZE*0,TEXT("1"));//ボタン生成 bt2 =createButton(hw,lp,(HMENU) BT_2,400, 20+CSIZE*1,TEXT("2"));//ボタン生成 bt3 =createButton(hw,lp,(HMENU) BT_3,400, 20+CSIZE*2,TEXT("3"));//ボタン生成 bt4 =createButton(hw,lp,(HMENU) BT_4,400, 20+CSIZE*3,TEXT("4"));//ボタン生成 bt1 =createButton(hw,lp,(HMENU) BT_5,400, 20+CSIZE*4,TEXT("5"));//ボタン生成 bt2 =createButton(hw,lp,(HMENU) BT_6,400, 20+CSIZE*5,TEXT("6"));//ボタン生成 bt3 =createButton(hw,lp,(HMENU) BT_7,400, 20+CSIZE*6,TEXT("7"));//ボタン生成 bt4 =createButton(hw,lp,(HMENU) BT_8,400, 20+CSIZE*7,TEXT("8"));//ボタン生成 bt1 =createButton(hw,lp,(HMENU) BT_9,400, 20+CSIZE*8,TEXT("9"));//ボタン生成 srand((int)time(NULL)); fontIni(); makeTb(20);IX=-1; IY=-1; InvalidateRect(hw,NULL,TRUE); } void procPaint(HWND hw, WPARAM wp, LPARAM lp){//画面に表示 PAINTSTRUCT ps; HDC hdc=BeginPaint(hw,&ps); DrawCell(hdc); EndPaint(hw, &ps); } void procLButtonUp(HWND hw, WPARAM wp, LPARAM lp){//ボタンクリックでマス目選択 int X=LOWORD(lp); int Y=HIWORD(lp); int MX=10+CSIZE*9; if(X<10 || X>MX || Y<10 || Y>MX) return; int IIX=(X-10)/CSIZE, IIY=(Y-10)/CSIZE; if(Q[IIY][IIX]!=0) return; IX=IIX; IY=IIY; InvalidateRect(hw,NULL,TRUE); } void procCommand(HWND hw, WPARAM wp, LPARAM lp){//ボタンクリックで数字選択 if(IX<0) return; int v=LOWORD(wp)-BT_1+1; if(v<1 || v>9)return; if(ansTb[IY][IX]!=v)MessageBox(hw,TEXT("不正解です"),TEXT("ナンプレ"), MB_OK); else { user[IY][IX]=v; IX=-1; InvalidateRect(hw,NULL,TRUE); if(!notEnd()){ MessageBox(hw,TEXT("終了しました。次のナンプレを生成します."),TEXT("ナンプレ"), MB_OK); initTb(); makeTb(20);IX=-1; IY=-1; InvalidateRect(hw,NULL,TRUE); } } InvalidateRect(hw,NULL,TRUE); } LRESULT CALLBACK WndProc(HWND hw, UINT msg, WPARAM wp, LPARAM lp){ switch(msg){ case WM_DESTROY : PostQuitMessage(0) ; return 0; case WM_CREATE : procCreate (hw, wp, lp); return 0; case WM_COMMAND : procCommand(hw, wp, lp); return 0; case WM_PAINT : procPaint (hw, wp, lp); return 0; case WM_LBUTTONUP : procLButtonUp(hw, wp, lp); } return DefWindowProc(hw, msg, wp, lp); }