<!DOCTYPE html>
<html>
<head>
<title>Omok Game Visualizer with Automatic Move Parsing</title>
<style>
body {
font-family: Arial, sans-serif;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
margin: 0;
background-color: #f4f4f4;
/* overflow-y: hidden; - 스크롤바를 숨기는 경우 */
}
.board {
display: grid;
grid-template-columns: repeat(14, 1fr);
grid-template-rows: repeat(14, 1fr);
border: 2px solid #333;
width: 420px;
height: 420px;
position: relative;
background-color: #ffebcd;
background-image:
linear-gradient(to right, #333 1px, transparent 1px),
linear-gradient(to bottom, #333 1px, transparent 1px);
background-size: 30px 30px;
margin-bottom: 5px; /* 버튼과의 간격 조절 */
}
.cell {
width: 100%;
height: 100%;
position: relative;
}
.stone {
width: 28px;
height: 28px;
border-radius: 50%;
position: absolute;
transform: translate(-50%, -50%);
}
.black {
background-color: black;
width: 32px;
height: 32px;
}
.white {
background-color: white;
border: 2px solid black;
}
.forbidden {
width: 20px;
height: 20px;
background-color: red;
position: absolute;
transform: translate(-50%, -50%);
border-radius: 50%;
}
.controls {
display: flex;
justify-content: center;
gap: 10px;
}
.controls button {
background-color: #4CAF50;
border: none;
color: white;
padding: 10px 15px; /* 버튼 크기 조절 */
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 14px; /* 글자 크기 조절 */
margin: 4px 2px;
transition-duration: 0.4s;
cursor: pointer;
border-radius: 8px;
}
.controls button:hover {
background-color: #45a049;
}
.controls button:disabled {
background-color: #cccccc;
cursor: not-allowed;
}
.status {
margin-top: 10px;
font-size: 18px;
}
</style>
</head>
<body>
<div class="board" id="board"></div>
<div class="controls">
<button onclick="reset()">Reset</button>
<button onclick="previousMove()">Previous Move</button>
<button onclick="nextMove()">Next Move</button>
<button onclick="lastMove()">Last Move</button>
<button id="playPauseBtn" onclick="togglePlayPause()">Play</button>
</div>
<div class="status" id="status"></div>
<script>
const inputMoves = "h8h9h6i8j7g9j8j9i9g6h7g8g5g10";
const gameRecord = parseMoves(inputMoves);
const black_stone = 1;
const white_stone = 2;
const empty = 0;
const board = document.getElementById('board');
const status = document.getElementById('status');
const playPauseBtn = document.getElementById('playPauseBtn');
let currentMove = -1;
let isPlaying = false;
let intervalId;
const delay = 1000; // 1 second delay for auto-play
// Renju rule logic
class Renju_Rule {
constructor(board) {
this.board = board;
this.board_size = board.length;
}
is_invalid(x, y) {
return (x < 0 || x >= this.board_size || y < 0 || y >= this.board_size);
}
set_stone(x, y, stone) {
this.board[y][x] = stone;
}
get_xy(direction) {
const list_dx = [-1, 1, -1, 1, 0, 0, 1, -1];
const list_dy = [0, 0, -1, 1, -1, 1, -1, 1];
return [list_dx[direction], list_dy[direction]];
}
get_stone_count(x, y, stone, direction) {
let x1 = x, y1 = y, cnt = 1;
for (let i = 0; i < 2; i++) {
const [dx, dy] = this.get_xy(direction * 2 + i);
x = x1, y = y1;
while (true) {
x += dx;
y += dy;
if (this.is_invalid(x, y) || this.board[y][x] !== stone) break;
else cnt++;
}
}
return cnt;
}
is_gameover(x, y, stone) {
for (let i = 0; i < 4; i++) {
const cnt = this.get_stone_count(x, y, stone, i);
if (cnt >= 5) return true;
}
return false;
}
is_six(x, y, stone) {
for (let i = 0; i < 4; i++) {
const cnt = this.get_stone_count(x, y, stone, i);
if (cnt > 5) return true;
}
return false;
}
is_five(x, y, stone) {
for (let i = 0; i < 4; i++) {
const cnt = this.get_stone_count(x, y, stone, i);
if (cnt === 5) return true;
}
return false;
}
is_five_or_more(x, y, stone) {
for (let i = 0; i < 4; i++) {
const cnt = this.get_stone_count(x, y, stone, i);
if (cnt >= 5) return true;
}
return false;
}
find_empty_point(x, y, stone, direction) {
const [dx, dy] = this.get_xy(direction);
while (true) {
x += dx;
y += dy;
if (this.is_invalid(x, y) || this.board[y][x] !== stone) break;
}
if (!this.is_invalid(x, y) && this.board[y][x] === empty) {
return [x, y];
} else {
return null;
}
}
open_three(x, y, stone, direction) {
for (let i = 0; i < 2; i++) {
const coord = this.find_empty_point(x, y, stone, direction * 2 + i);
if (coord) {
const [dx, dy] = coord;
this.set_stone(dx, dy, stone);
if (1 === this.open_four(dx, dy, stone, direction)) {
if (!this.forbidden_point(dx, dy, stone)) {
this.set_stone(dx, dy, empty);
return true;
}
}
this.set_stone(dx, dy, empty);
}
}
return false;
}
open_four(x, y, stone, direction) {
if (this.is_five(x, y, stone)) {
return false;
}
let cnt = 0;
for (let i = 0; i < 2; i++) {
const coord = this.find_empty_point(x, y, stone, direction * 2 + i);
if (coord) {
if (this.five(coord[0], coord[1], stone, direction)) {
cnt++;
}
}
}
if (cnt === 2) {
if (4 === this.get_stone_count(x, y, stone, direction)) {
cnt = 1;
}
} else cnt = 0;
return cnt;
}
four(x, y, stone, direction) {
for (let i = 0; i < 2; i++) {
const coord = this.find_empty_point(x, y, stone, direction * 2 + i);
if (coord) {
if (this.five(coord[0], coord[1], stone, direction)) {
return true;
}
}
}
return false;
}
five(x, y, stone, direction) {
return 5 === this.get_stone_count(x, y, stone, direction);
}
double_three(x, y, stone) {
let cnt = 0;
this.set_stone(x, y, stone);
for (let i = 0; i < 4; i++) {
if (this.open_three(x, y, stone, i)) cnt++;
}
this.set_stone(x, y, empty);
return cnt >= 2;
}
double_four(x, y, stone) {
let cnt = 0;
this.set_stone(x, y, stone);
for (let i = 0; i < 4; i++) {
if (this.open_four(x, y, stone, i) === 2) cnt += 2;
else if (this.four(x, y, stone, i)) cnt++;
}
this.set_stone(x, y, empty);
return cnt >= 2;
}
forbidden_point(x, y, stone) {
if (this.is_five(x, y, stone)) return false;
if (this.is_six(x, y, stone)) return true;
if (this.double_three(x, y, stone) || this.double_four(x, y, stone)) return true;
return false;
}
}
const gameBoard = Array.from({ length: 14 }, () => Array(14).fill(empty));
const renjuRule = new Renju_Rule(gameBoard);
function parseMoves(moveString) {
const moveArray = [];
const regex = /[a-zA-Z][0-9]{1,2}/g;
let matches;
while ((matches = regex.exec(moveString)) !== null) {
moveArray.push([
14 - parseInt(matches[0].substring(1)), // Row index (1=14, 2=13, ...)
matches[0].charCodeAt(0) - 'a'.charCodeAt(0) - 1 // Column index (A=0, B=1, ...)
]);
}
return moveArray;
}
function createBoard() {
board.innerHTML = '';
for (let i = 0; i < 14; i++) {
for (let j = 0; j < 14; j++) {
const cell = document.createElement('div');
cell.classList.add('cell');
cell.id = `cell-${i}-${j}`;
board.appendChild(cell);
}
}
}
function placeStone(row, col, color) {
const stone = document.createElement('div');
stone.classList.add('stone', color);
stone.style.top = `${(row + 1) * 30}px`;
stone.style.left = `${(col + 1) * 30}px`;
board.appendChild(stone);
}
function placeForbidden(row, col) {
const forbidden = document.createElement('div');
forbidden.classList.add('forbidden');
forbidden.style.top = `${(row + 1) * 30}px`;
forbidden.style.left = `${(col + 1) * 30}px`;
board.appendChild(forbidden);
}
function updateBoard() {
createBoard();
for (let i = 0; i <= currentMove; i++) {
const move = gameRecord[i];
const color = i % 2 === 0 ? 'black' : 'white';
placeStone(move[0], move[1], color);
gameBoard[move[0]][move[1]] = color === 'black' ? black_stone : white_stone;
}
if (currentMove >= 0 && currentMove % 2 === 1) { // Check only for white's turn
for (let i = 0; i < 14; i++) {
for (let j = 0; j < 14; j++) {
if (gameBoard[i][j] === empty && renjuRule.forbidden_point(j, i, black_stone)) {
placeForbidden(i, j);
}
}
}
}
status.textContent = `Move ${currentMove + 1} of ${gameRecord.length}`;
}
function reset() {
currentMove = -1;
gameBoard.forEach(row => row.fill(empty)); // 게임 보드를 초기 상태로 재설정
updateBoard();
if (isPlaying) {
togglePlayPause();
}
}
function previousMove() {
currentMove = Math.max(currentMove - 1, -1);
updateBoard();
}
function nextMove() {
currentMove = Math.min(currentMove + 1, gameRecord.length - 1);
updateBoard();
}
function lastMove() {
currentMove = gameRecord.length - 1;
updateBoard();
}
function togglePlayPause() {
isPlaying = !isPlaying;
if (isPlaying) {
intervalId = setInterval(nextMove, delay);
playPauseBtn.textContent = 'Pause';
} else {
clearInterval(intervalId);
playPauseBtn.textContent = 'Play';
}
}
createBoard();
updateBoard();
</script>
</body>
</html>
혹시 사용하는 분이 있을까 소스
h8h9h6i8j7g9j8j9i9g6h7g8g5g10 이 부분을 나에 맞게 기보를 넣으면 됩니다.
렌주넷같은 곳에서 하단부분의 역삼각형을 클릭하면 아래 수순이 나오는 기보를 얻을 수 있는데 위의 코드에 저걸 넣고 html파일로 저장하면 웹브라우저로 볼 수 있습니다.
수순을 진행하면 흑차례 금수가 되는 지점은 붉은색 원 확인이 됩니다.
반응형