오델로 프로그래밍
새 기보 게시판에 사용된 자바 스크립스 소스입니다.
클릭한대로 수가 둬지고 히스토리 저장으로 이전수 이동이 가능한 소스입니다.
예제는 http://www.othelloclub.com/othellojava/othellogibo.htm 에 있습니다.
원리는 다음과 같습니다.
8x8 총 64칸의 테이블을 만들고 각 칸에 오델로 초기배치 대로 이미지를 배치합니다.
(중앙 네곳만 흰색, 검은색이 교차하게 나머지는 그냥 빈 판) 각 이미지에 명칭을 부여합니다. (c11, c12 이런식으로..)
시작할때 턴은 흑 차례이고, 마우스 클릭할때마다 턴은 바뀝니다.(패스 판정후 둘곳이 없으면 다시 턴 바꿈)
마우스로 클릭을 하면 둔곳에 지금 턴인 색깔의 돌로 이미지로 바꾸고,
주위 8방향으로 검색으로 해서 돌아가는 돌을 계산해서 돌립니다.
* 이미지를 바꾸는 함수 - RawPutPiece 함수
* 마우스 클릭한곳 및 돌아가는 돌 계산해서 칸 이미지 체인지 - FlipPieces 함수
모든 계산이 완료 되고 판 구성이 완료되면 현재의 64칸을 하나의 변수로 배열에 저장합니다.
(빈칸은 0, 흑돌은 1, 흰돌은 2, 방금 둔 흑돌은 5, 방금둔 흰돌은 6, 이번수로 뒤집힌 흑돌은 7, 이번수로 뒤집힌 백돌은 8)
0,0,0,0,0,0,1,2,0,0,0,0 이런식으로 64칸을 표시하고 마지막칸 다음에 지금 둔 차례(흑 0, 백 1)를 넣은후 이걸 하나의 변수로 저장하여
크기 60짜리의 배열에 넣습니다. (총 60수이므로)
*history 저장 변수 - boardhistory[] 배열 (크기 60)
이전수 보기를 했을땐 boardhistory 배열을 하나전으로 돌려서
(이번수가 boardhistory[10] 에 저장되는거였다면 boardhistory[9]로)
그 배열에 저장되어 있는 64칸으로 판 구성을 바꿉니다.(이전수의 판 상황이므로)
차례 역시 이전 배열(여기에선 boardhistory[9])의 마지막 값으로 바꿔줍니다.
*이전수 보기 함수 - returnmove 함수
글 작성 완료 버튼을 누르면
이 boardhistory[] 배열이 db로 보내져서 하나의 기보가 올라가게 됩니다.
기본적인 알고리즘은 위와 같습니다.
오델로판의 처음 상태에서 시작해서 두는대로 게임이 진행되고,
이전수로 돌릴수도 있는 히스토리 기능이 포함되어 있습니다.
소스는 다음과 같습니다.
소스에는 othellogibo.js 와 othellogibo.htm 이렇게 두개의 소스가 있습니다.
othellogibo.js엔 기능적인 소스가 다 포함되어 있고, othellogibo.htm에서는 오델로판을 출력해주는 역할을 합니다.
소스가 상당히 깁니다 ^^;
othellogibo.js 소스
var EMPTY = 0;
var BLACK = 1;
var WHITE = 2;
var BLACKTRANS = 3;
var WHITETRANS = 4;
var BLACKL = 5;
var WHITEL = 6;
var BLACKFLIP=7;
var WHITEFLIP=8;
var movenum = 0;
var piecewidth = 38;
var pieceheight = 38;
var width = 8;
var height = 8;
var showcursor = true;
var picture = new Array();
var board = new Array(width);
var boardset = new Array(width);
var boardhistory = new Array(61);
var turn = BLACK;
var tempx;
var tempy;
var tempp;
var tempplaymove;
var tempx2;
var tempy2;
var tempp2;
var lastlookup = 0;
//img name을 document.images[i] 의 i값으로 바꿔줌
function lookup(name) {
for(i = lastlookup; i < document.images.length; i++)
if(document.images[i].name == name) { lastlookup = i; return(i); }
for(i = 0; i < lastlookup; i++)
if(document.images[i].name == name) { lastlookup = i; return(i); }
return(-1);
}
//piece 설정
function piece(imagename) {
this.imagenum = lookup(imagename);
}
//이미지 불러오기
function LoadPieceImage(picnum, pictureURL) {
picture[picnum] = new Image();
picture[picnum].src = pictureURL;
}
//돌 이미지 체인지
function SetPieceImage(x, y, src) {
if(document.images[board[x][y].imagenum].src != src) {
document.images[board[x][y].imagenum].src = src;
}
}
//판 초기화
function InitializeBoard(color) {
for(x = 0; x < width; x++){
board[x] = new Array (height);
boardset[x] = new Array (height);
}
for(x=0;x<61;x++)
boardhistory[x] ="";
document.write('<TABLE CELLPADDING="0" CELLSPACING="1" BORDER="0" COLS="' + width + '" ROWS="' + height + '" bgcolor = black>');
for(y = 0; y < height; y++) {
document.write('<TR>');
for(x = 0; x < width; x++) {
if((x == 3 && y == 3) || (x == 4 && y == 4)) player = WHITE;
else if((x == 3 && y == 4) || (x == 4 && y == 3)) player = BLACK;
else player = EMPTY;
document.write('<TD BGCOLOR="' + color + '" WIDTH="' + piecewidth + '" HEIGHT="' + pieceheight + '"><A HREF="javascript:void(0);" onClick="PutPiece(' + x + ', ' + y + ')" onMouseOver="CheckPutPiece(' + x + ', ' + y + ')" onMouseOut="RestorePiece(' + x + ', ' + y + ')" onfocus = blur();><IMG NAME="c[' + x + ',' + y + ']" SRC="' + picture[player].src + '" BORDER="0" HEIGHT="' + pieceheight + '" WIDTH="' + piecewidth + '"></A></TD>');
board[x][y] = new piece('c[' + x + ',' + y + ']');
boardset[x][y] = player;
boardhistory[0] += player+",";
}
document.writeln('</TR>');
}
document.write('</TABLE><div align = center><A HREF="javascript:void(0);" onClick="ResetBoard()" onfocus = blur();><font color = #f0f0f0>판 지우기</a> | <A HREF="javascript:void(0);" onClick="Returnmove()" onfocus = blur();><font color = #f0f0f0>◀ 이전으로</font></A></div>');
}
//판 리셋
function ResetBoard() {
movenum = 0;
for(x=0;x<61;x++)
boardhistory[x] ="";
for(y = 0; y < height; y++) {
for(x = 0; x < width; x++) {
boardset[x][y] = EMPTY;
SetPieceImage(x, y, picture[EMPTY].src);
}
}
document.write.transcript.value = ""
RawPutPiece(3, 3, WHITE);
RawPutPiece(4, 4, WHITE);
RawPutPiece(3, 4, BLACK);
RawPutPiece(4, 3, BLACK);
for(y = 0; y < height; y++) {
for(x = 0; x < width; x++) {
boardhistory[0] += boardset[x][y]+',' ;
}
}
turn = BLACK;
checknum();
}
//돌아가는 돌 개수 계산
function NumFlips(x, y, player) {
var playerl;
var playerflip;
if(player == WHITE) {
playerl = WHITEL;
playerflip = WHITEFLIP;
}
else{
playerl = BLACKL;
playerflip = BLACKFLIP;
}
var deltax, deltay, distance;
var posx, posy;
var count = 0;
for(deltay = -1; deltay <= 1; deltay++) {
for(deltax = -1; deltax <= 1; deltax++) {
for(distance = 1;; distance++) {
posx = x + (distance * deltax);
posy = y + (distance * deltay);
// 판 밖으로 벗어나면 멈춤
if(posx < 0 || posx >= width || posy < 0 || posy >= height)
break;
// 빈칸에 도달하면 멈춤
if(boardset[posx][posy] == EMPTY)
break;
// player랑 같은 색을 만나면 돌아가는 돌 계산
if((boardset[posx][posy] == player)||(boardset[posx][posy] == playerl)||(boardset[posx][posy] == playerflip)) {
count += distance - 1;
break;
}
}
}
}
return(count);
}
//지정한 좌표에 지정한 돌의 색깔과 이미지 넣기
function RawPutPiece(x, y, player) {
boardset[x][y] = player;
SetPieceImage(x, y, picture[player].src);
}
//수 두기, 돌아가는 돌들 계산
function FlipPieces(x, y, player) {
if(movenum == 0){
boardhistory[0] += 1;
document.write.transcript.value += boardhistory[0]+'|';
}
movenum++;
var deltax, deltay, distance;
var posx, posy;
for (u =0;u<width ;u++ )
{
for(w=0;w<width;w++){
if(boardset[w][u] == WHITEFLIP) RawPutPiece(w, u, WHITE);
if(boardset[w][u] == BLACKFLIP) RawPutPiece(w, u, BLACK);
if(boardset[w][u] == WHITEL) RawPutPiece(w, u, WHITE);
if(boardset[w][u] == BLACKL) RawPutPiece(w, u, BLACK);
}
}
if(player == 1) playmove = 5;
else playmove = 6;
RawPutPiece(x, y, player);
var checkt = 0;
var u,w;
for(deltay = -1; deltay <= 1; deltay++) {
for(deltax = -1; deltax <= 1; deltax++) {
for(distance = 1;; distance++) {
posx = x + (distance * deltax);
posy = y + (distance * deltay);
// 판밖으로 벗어나면 멈춤
if(posx < 0 || posx >= width || posy < 0 || posy >= height)
break;
// 빈캄에 도달하면 멈춤
if(boardset[posx][posy] == EMPTY)
break;
// player랑 같은 색을 만나면 돌아가는 돌 계산
if((boardset[posx][posy] == player)) {
if(distance!=1){
if(checkt!=0)
checkt++;
}
var flipplayer;
if(player == WHITE) flipplayer = WHITEFLIP;
else flipplayer = BLACKFLIP;
for(distance--; distance > 0; distance--) {
posx = x + (distance * deltax);
posy = y + (distance * deltay);
RawPutPiece(posx, posy, player);
boardset[posx][posy] = flipplayer;
}
break;
}
}
}
}
RawPutPiece(x, y, playmove);
for (u =0;u<width ;u++ )
{
for(w=0;w<width;w++){
boardhistory[movenum] += boardset[w][u]+",";
}
}
}
//이전수 보여주기
function Returnmove(){
if(movenum > 0){
document.write.transcript.value = document.write.transcript.value.replace(boardhistory[movenum]+"|","");
boardhistory[movenum] ='';
movenum--;
var backmove=boardhistory[movenum].split(",");;
var i,j;
var cut;
for (i=0;i<width ;i++)
{
for (j=0;j<width ;j++)
{
cut = eval(8*i+j);
RawPutPiece(j, i,backmove[cut] );
if(boardset[j][i]== BLACKFLIP) RawPutPiece(j, i,BLACK);
if(boardset[j][i]== WHITEFLIP) RawPutPiece(j, i,WHITE);
}
}
if(movenum == 0)
turn = BLACK;
else{
turn = backmove[64];
}
}
}
//가능한 수가 있는지 체크
function AnyMoves(player) {
var x, y;
for(y = 0; y < height; y++) {
for(x = 0; x < width; x++) {
if(boardset[x][y] != EMPTY) continue;
if(NumFlips(x, y, player) > 0) return(true);
}
}
return(false);
}
//수를 둘수 있는곳인지 체크
function CanPutPiece(x, y, player) {
if(turn != player)
return(false);
if(boardset[x][y] != EMPTY)
return(false);
return(NumFlips(x, y, player) > 0);
}
//마우스 포인터가 over 됐을때 수를 둘수 있는곳이면 TRANS 이미지를 보여줌
function CheckPutPiece(x, y) {
var over;
if(! showcursor) return;
if(! CanPutPiece(x, y, turn)) return;
if(turn == WHITE) over = WHITETRANS;
else over = BLACKTRANS;
SetPieceImage(x, y, picture[over].src);
}
//마우스포인터가 out 됐을때 이미지 복구
function RestorePiece(x, y) {
var backsrc;
if(boardset[x][y] == BLACKFLIP) backsrc = picture[BLACK].src;
else if(boardset[x][y] == WHITEFLIP) backsrc = picture[WHITE].src;
else backsrc = picture[boardset[x][y]].src
SetPieceImage(x, y, backsrc);
}
//수 두기
function PutPiece(x, y) {
if(! CanPutPiece(x, y, turn)) return;
FlipPieces(x, y, turn);
DoneTurn();
}
//상대 플레이어 지정
function OtherPlayer(player) {
if(player == WHITE)
player = BLACK;
else
player = WHITE;
return(player);
}
//턴 완료
function DoneTurn() {
var moves = AnyMoves(turn);
turn = OtherPlayer(turn);
if(! AnyMoves(turn)) {
if(! moves) return(GameOver());
turn = OtherPlayer(turn);
}
checknum();
boardhistory[movenum]+= turn;
document.write.transcript.value += boardhistory[movenum]+"|";
}
//스코어 계산
function checknum(){
var x,y;
var blacknum = 0;
var whitenum = 0;
for(y=0;y<width ;y++){
for(x=0;x<width;x++){
if((boardset[x][y] == WHITE)||(boardset[x][y]==WHITEL)||(boardset[x][y]==WHITEFLIP))
whitenum++;
else if(boardset[x][y] ==EMPTY){}
else blacknum++;
}
}
document.write.winlose.value = blacknum +' - '+whitenum;
}
//게임 종료
function GameOver() {
document.write.transcript.value += boardhistory[movenum]+"|";
checknum();
}
othellogibo.htm 소스
<SCRIPT language=JavaScript1.2
src="othellogibo.js"></SCRIPT>
<SCRIPT language=JavaScript1.2>
LoadPieceImage(EMPTY,"images/blank.gif");
LoadPieceImage(BLACK, "images/black.gif");
LoadPieceImage(WHITE,"images/white.gif");
LoadPieceImage(BLACKTRANS, "images/black-trans.gif");
LoadPieceImage(WHITETRANS, "images/white-trans.gif");
LoadPieceImage(BLACKL, "images/blackl.gif");
LoadPieceImage(WHITEL, "images/whitel.gif");
LoadPieceImage(BLACKFLIP, "images/blackflip.gif");
LoadPieceImage(WHITEFLIP, "images/whiteflip.gif");
InitializeBoard("green");
// -->
</SCRIPT>
<body bgcolor = 292929>
<form method=post name=write enctype=multipart/form-data>
<input type=hidden name=transcript size = 100 value=""><br>
<input type=hidden name="winlose" value="" >
</form>
------------------------------------------------------------
위 두 소스를 저장하여 계정의 같은 폴더에 올리고 othellogibo.htm을 실행하면 실행이 됩니다.






