복잡한 게임 로직을 위한 JavaScript 스타일 스크립팅
레벨 3: Script (스크립트)
레벨 3은 선언적으로 표현할 수 없는 복잡한 게임 로직을 구현하기 위한 JavaScript 스타일 스크립팅 언어를 제공합니다.
개요
레벨 3이 유용한 경우:
- 복잡한 AI 동작
- 커스텀 이동 유효성 검사
- 동적 기물 능력
- 고급 게임 상태 조작
- 외부 시스템과의 통합
스크립트 블록
스크립팅 코드는 script 블록 안에 작성합니다:
chesslang
script {
// JavaScript 스타일 코드를 여기에 작성
}변수와 타입
변수 선언
chesslang
script {
let x = 5; // 변경 가능한 변수
const y = 10; // 변경 불가능한 상수
let name = "Rook"; // 문자열
let active = true; // 불리언
let pieces = []; // 배열
let config = {}; // 객체
}타입 추론
타입은 자동으로 추론됩니다:
chesslang
script {
let count = 0; // number
let message = "Hello"; // string
let valid = true; // boolean
let pos = { x: 1, y: 2 }; // object
}제어 흐름
조건문
chesslang
script {
if (piece.type == "King") {
// 킹 관련 로직
} else if (piece.type == "Queen") {
// 퀸 관련 로직
} else {
// 다른 기물
}
}반복문
chesslang
script {
// For 루프
for (let i = 0; i < 8; i++) {
// 각 파일 처리
}
// For-of 루프
for (const piece of board.pieces) {
// 각 기물 처리
}
// While 루프
while (condition) {
// 조건이 거짓이 될 때까지 반복
}
}함수
함수 선언
chesslang
script {
function calculateValue(piece) {
if (piece.type == "Queen") return 9;
if (piece.type == "Rook") return 5;
if (piece.type == "Bishop") return 3;
if (piece.type == "Knight") return 3;
if (piece.type == "Pawn") return 1;
return 0;
}
}화살표 함수
chesslang
script {
const isEnemy = (piece, player) => piece.owner != player;
const sumValues = (pieces) => {
let total = 0;
for (const p of pieces) {
total += calculateValue(p);
}
return total;
};
}내장 객체
board
게임 보드에 접근합니다:
chesslang
script {
// 위치의 기물 가져오기
const piece = board.at({ file: 4, rank: 0 });
// 모든 기물 가져오기
const allPieces = board.pieces;
// 플레이어별 기물 가져오기
const whitePieces = board.pieces.filter(p => p.owner == "White");
// 칸이 비어있는지 확인
const isEmpty = board.isEmpty({ file: 3, rank: 3 });
// 인접 칸 가져오기
const neighbors = board.adjacent({ file: 4, rank: 4 });
}game
게임 상태에 접근합니다:
chesslang
script {
// 현재 플레이어
const current = game.currentPlayer;
// 이동 기록
const moves = game.moveHistory;
const lastMove = game.lastMove;
// 턴 번호
const turn = game.turnNumber;
// 체크 상태
const inCheck = game.isCheck(player);
const isCheckmate = game.isCheckmate(player);
// 게임 결과
const isOver = game.isOver;
const winner = game.winner;
}위치 유틸리티
chesslang
script {
// 칸 표기법 파싱
const pos = parseSquare("e4"); // { file: 4, rank: 3 }
// 표기법으로 변환
const notation = toSquare({ file: 4, rank: 3 }); // "e4"
// 거리 계산
const dist = distance(pos1, pos2);
// 방향으로 칸 가져오기
const ray = getRay(pos, "N", 3); // 북쪽으로 3칸
// 위치 유효성 확인
const valid = isValidPos({ file: 8, rank: 0 }); // false
}이벤트 핸들러
글로벌 이벤트 핸들러
chesslang
script {
game.on("move", function(event) {
console.log(event.piece.type, "moved from", event.from, "to", event.to);
});
game.on("capture", function(event) {
console.log(event.captured.type, "was captured!");
});
game.on("turnEnd", function(event) {
// 특수 조건 확인
if (game.turnNumber > 50) {
// 시간 압박 효과 적용
}
});
}완전한 예제
Progressive Chess
chesslang
game: "Progressive Chess"
extends: "Standard Chess"
script {
// 턴당 이동 추적
game.state.movesThisTurn = 0;
game.state.movesRequired = 1;
game.on("move", function(event) {
game.state.movesThisTurn++;
// 체크 확인 - 턴 즉시 종료
if (game.isCheck(event.player === "White" ? "Black" : "White")) {
game.endTurn();
return;
}
// 필요한 이동 완료 확인
if (game.state.movesThisTurn >= game.state.movesRequired) {
game.endTurn();
}
});
game.on("turnEnd", function(event) {
// 다음 플레이어는 이동 하나 더
game.state.movesRequired++;
game.state.movesThisTurn = 0;
});
}Countdown Chess
chesslang
game: "Countdown Chess"
extends: "Standard Chess"
script {
game.state.whiteMoves = 50;
game.state.blackMoves = 50;
game.on("move", function(event) {
if (event.player === "White") {
game.state.whiteMoves--;
console.log("White moves left:", game.state.whiteMoves);
if (game.state.whiteMoves <= 0) {
game.declareWinner("Black", "White ran out of moves");
}
} else {
game.state.blackMoves--;
console.log("Black moves left:", game.state.blackMoves);
if (game.state.blackMoves <= 0) {
game.declareWinner("White", "Black ran out of moves");
}
}
game.endTurn();
});
}Material Race
chesslang
game: "Material Race"
extends: "Standard Chess"
script {
game.state.whiteCaptures = 0;
game.state.blackCaptures = 0;
var pieceValues = {
"Pawn": 1,
"Knight": 3,
"Bishop": 3,
"Rook": 5,
"Queen": 9
};
game.on("capture", function(event) {
var capturedType = event.captured.type;
var value = pieceValues[capturedType] || 0;
if (event.player === "White") {
game.state.whiteCaptures += value;
console.log("White captures:", game.state.whiteCaptures);
if (game.state.whiteCaptures >= 15) {
game.declareWinner("White", "Captured 15 points");
}
} else {
game.state.blackCaptures += value;
console.log("Black captures:", game.state.blackCaptures);
if (game.state.blackCaptures >= 15) {
game.declareWinner("Black", "Captured 15 points");
}
}
});
}디버깅
콘솔 출력
chesslang
script {
console.log("Debug message");
console.warn("Warning message");
console.error("Error message");
// 객체 로그
console.log("Piece:", piece);
console.log("Board state:", board.toJSON());
}모범 사례
- 스크립트를 집중적으로 유지: 선언적 로직은 레벨 2, 복잡한 절차는 레벨 3 사용
- 함수 문서화: 함수가 하는 일을 설명하는 주석 추가
- 오류 처리: null/undefined 값 확인
- 부작용 방지: 함수는 예측 가능해야 함
- 철저히 테스트: 스크립트는 미묘한 버그를 유발할 수 있음
제한 사항
- async/await 없음 (게임 로직은 동기적)
- DOM 접근 없음 (격리된 컨텍스트에서 실행)
- 외부 import 없음 (내장 유틸리티 사용)
- 제한된 재귀 깊이 (무한 루프 방지)