커스텀 기물, 효과, 트리거를 위한 선언적 DSL

레벨 2: Compose (조합)

레벨 2는 커스텀 기물, 보드 효과, 게임 트리거를 만들기 위한 선언적 DSL을 도입합니다. 여기서 ChessLang의 진정한 힘이 발휘됩니다.

개요

레벨 2로 가능한 것들:

  • 고유한 이동 패턴을 가진 커스텀 기물 정의
  • 게임 동작을 수정하는 보드 효과 생성
  • 게임 이벤트에 반응하는 트리거 설정
  • 복잡한 상호작용을 선언적으로 조합

커스텀 기물

기본 기물 정의

chesslang
piece Amazon {
  move: slide(orthogonal) | slide(diagonal) | leap(2, 1)
  capture: =move
}

이렇게 하면 퀸과 나이트의 이동을 결합한 "Amazon"이 생성됩니다.

기물 속성

속성필수설명
move이동 패턴
capture아니오잡기 패턴 (기본값: move와 동일)
traits아니오특수 능력
state아니오기물 고유 상태
value아니오기물 가치 (평가용)
symbol아니오표시 심볼

이동 패턴

step

고정된 수의 칸을 이동합니다:

chesslang
piece King {
  move: step(any)              # 모든 방향으로 1칸
}

piece Pawn {
  move: step(forward)          # 앞으로 1칸
  move: step(forward, 2) where first_move and clear
}

slide

막힐 때까지 여러 칸을 이동합니다:

chesslang
piece Rook {
  move: slide(orthogonal)      # 북/남/동/서 방향으로 무제한
}

piece Bishop {
  move: slide(diagonal)        # 대각선으로 무제한
}

piece Queen {
  move: slide(orthogonal) | slide(diagonal)
}

leap

특정 오프셋으로 점프합니다 (중간 기물 무시):

chesslang
piece Knight {
  move: leap(2, 1)             # 8가지 회전 모두 자동 포함
}

piece Camel {
  move: leap(3, 1)             # 더 긴 점프
}

piece Zebra {
  move: leap(3, 2)
}

hop

기물을 뛰어넘어 반대편에 착지합니다:

chesslang
piece Cannon {
  move: slide(orthogonal)
  capture: hop(orthogonal)     # 정확히 하나의 기물을 뛰어넘어야 함
}

패턴 조합자

Or (|)

패턴 중 선택:

chesslang
piece Queen {
  move: slide(orthogonal) | slide(diagonal)
}

Then (+)

순차적 이동:

chesslang
piece Gryphon {
  move: step(diagonal) + slide(orthogonal)
}

Repeat (*)

패턴 반복:

chesslang
piece Sprinter {
  move: step(forward) * 3 where clear
}

조건 (where)

패턴에 조건을 추가합니다:

chesslang
piece Pawn {
  move: step(forward) where empty
  move: step(forward, 2) where first_move and clear
  capture: step(forward-diagonal) where enemy
}

사용 가능한 조건:

조건설명
empty대상 칸이 비어있음
enemy대상 칸에 적 기물이 있음
friend대상 칸에 아군 기물이 있음
clear경로에 기물이 없음
first_move기물의 첫 이동
check킹이 체크 상태
not check킹이 체크 상태가 아님

잡기 패턴

chesslang
piece Pawn {
  move: step(forward)
  capture: step(forward-diagonal)    # 다른 잡기 패턴
}

piece Queen {
  move: slide(any)
  capture: =move                     # move 패턴과 동일
}

특성 (Traits)

특성은 기물에 특수 능력을 부여합니다:

chesslang
piece Knight {
  move: leap(2, 1)
  traits: [jump]    # 기물을 뛰어넘을 수 있음
}

piece King {
  move: step(any)
  traits: [royal]   # 잡히면/체크메이트되면 게임 종료
}

내장 특성:

특성설명
royal잡히면 패배 (체크메이트 대상)
jump다른 기물을 뛰어넘을 수 있음
phase다른 기물을 통과할 수 있음 (잡기 불가)
promote끝에 도달하면 승진 가능
immune잡히지 않음
explosive잡히면 주변 기물을 파괴

커스텀 특성도 정의할 수 있습니다:

chesslang
piece Teleporter {
  move: leap(2, 1)
  traits: [warp, jump]     # 'warp'는 커스텀 특성
  state: { cooldown: 0 }
}

기물 상태

기물 고유 정보를 추적합니다:

chesslang
piece Berserker {
  move: step(any)
  state: { rage: 0 }
}

효과 (Effects)

효과는 보드의 칸을 수정합니다:

chesslang
effect fire {
  damages: [any]              # 모든 기물에게 피해
  visual: highlight(red)
}

effect ice {
  blocks: [enemy]             # 적 이동을 막음
  visual: highlight(blue)
}

effect trap {
  on_enter: capture(entering) # 들어오는 기물을 잡음
  visual: highlight(orange)
}

효과 속성

속성설명
damages피해를 받는 기물
blocks진입할 수 없는 기물
on_enter기물이 들어올 때 액션
on_exit기물이 나갈 때 액션
duration효과 지속 시간 (턴)
visual시각적 표현

시각 효과

chesslang
effect glow {
  visual: highlight(yellow)
  visual: border(gold)
  visual: icon("star")
}

트리거 (Triggers)

트리거는 게임 이벤트에 반응합니다:

chesslang
trigger explosion {
  on: capture
  when: piece.type == Bomb
  do: remove pieces in radius(1) from target
}

여러 액션이 필요한 경우 중괄호 블록을 사용합니다:

chesslang
trigger multi_action {
  on: capture
  do: {
    remove piece
    set game.state.score = game.state.score + 1
  }
}

트리거 구조

chesslang
trigger name {
  on: event           # 언제 발동할지
  when: condition     # 추가 조건
  do: action          # 무엇을 할지
}

이벤트

이벤트설명사용 가능한 변수
move모든 이동piece, origin, target
capture기물 잡힘piece, captured, origin, target
check체크 줌piece, king
turn_start턴 시작player
turn_end턴 종료player
game_start게임 시작-

액션

chesslang
trigger promote_on_hill {
  on: move
  when: piece.type == Pawn and target in zone.hill
  do: transform piece to Queen
}

trigger place_trap {
  on: move
  when: piece.type == Trapper
  do: mark origin with trap
}

trigger rage_increase {
  on: capture
  when: piece.type == Berserker
  do: set piece.state.rage = piece.state.rage + 1
}

사용 가능한 액션:

액션설명
set X = Y값 설정
create Piece at pos새 기물 생성
remove piece기물 제거
transform piece to Type기물 타입 변경
mark pos with effect칸에 효과 적용
win Player승자 선언
draw무승부 선언

완전한 예제

Atomic Chess

chesslang
game: "Atomic Chess"
extends: "Standard Chess"

trigger atomic_explosion {
  on: capture
  do: {
    remove piece
    remove captured
    remove pieces in radius(1) from target where not Pawn
  }
}

victory:
  add:
    king_exploded: opponent.King == null

Trapper Chess

chesslang
game: "Trapper Chess"
extends: "Standard Chess"

piece Trapper {
  move: step(any)
  capture: =move
  state: { traps_placed: 0 }
  value: 3
}

effect trap {
  on_enter: capture(entering) where enemy
  duration: 5
  visual: highlight(red, 0.3)
}

trigger place_trap {
  on: move
  when: piece.type == Trapper and piece.state.traps_placed < 3
  do: {
    mark origin with trap
    set piece.state.traps_placed = piece.state.traps_placed + 1
  }
}

setup:
  add:
    White:
      Trapper: [d1]
    Black:
      Trapper: [d8]

Cannon Chess

chesslang
game: "Cannon Chess"
extends: "Standard Chess"

piece Cannon {
  move: slide(orthogonal)
  capture: hop(orthogonal)   # 정확히 하나의 기물을 뛰어넘어야 함
  value: 5
  symbol: "C"
}

setup:
  modify:
    White:
      Rook: [a1]
      Cannon: [h1]
    Black:
      Rook: [a8]
      Cannon: [h8]

Berserk Chess

chesslang
game: "Berserk Chess"
extends: "Standard Chess"

piece Berserker {
  move: step(any) * (1 + state.rage)
  capture: =move
  state: { rage: 0 }
  value: 4
}

trigger gain_rage {
  on: capture
  when: piece.type == Berserker
  do: set piece.state.rage = min(piece.state.rage + 1, 3)
}

trigger lose_rage {
  on: turn_end
  when: any Berserker where state.rage > 0
  do: set piece.state.rage = piece.state.rage - 1
}

setup:
  modify:
    White:
      Knight: [b1]
      Berserker: [g1]
    Black:
      Knight: [b8]
      Berserker: [g8]

고급 커스텀 기물

더 복잡한 동작을 가진 기물들의 예제입니다.

Vampire (뱀파이어)

잡은 기물을 아군으로 변환하는 강력한 기물입니다.

chesslang
piece Vampire {
  move: step(any) | leap(2, 0)
  capture: =move
  traits: [jump]
  state: { thralls: 0 }
  value: 6
}

trigger vampiric_conversion {
  on: capture
  when: piece.type == Vampire and captured.type != King
  do: {
    transform captured to captured.type
    set captured.owner = piece.owner
    set piece.state.thralls = piece.state.thralls + 1
  }
}

Shapeshifter (변신체)

마지막으로 잡은 기물의 이동 패턴을 복사합니다.

chesslang
piece Shapeshifter {
  move: leap(2, 1)              # 기본: 나이트 이동
  capture: =move
  traits: [jump]
  state: { 
    copiedType: "Knight",
    captureCount: 0 
  }
  value: 5
}

trigger shapeshifter_copy {
  on: capture
  when: piece.type == Shapeshifter
  do: {
    set piece.state.copiedType = captured.type
    set piece.state.captureCount = piece.state.captureCount + 1
  }
}

Guardian (수호자)

인접한 아군 기물을 잡기로부터 보호합니다.

chesslang
piece Guardian {
  move: step(any)
  capture: =move
  state: { protected: 0 }
  value: 4
}

trigger guardian_protection {
  on: capture
  when: any Guardian adjacent to captured where Guardian.owner == captured.owner
  do: {
    cancel    # 잡기 취소 - 기물이 보호됨
    set Guardian.state.protected = Guardian.state.protected + 1
  }
}

Lancer (기병)

전방으로 돌격하여 여러 기물을 잡을 수 있습니다.

chesslang
piece Lancer {
  move: step(orthogonal)                   # 일반 이동
  capture: slide(forward) where enemy      # 돌격 공격
  traits: [charge]
  state: { chargeReady: true }
  value: 5
}

trigger lancer_cooldown {
  on: capture
  when: piece.type == Lancer
  do: set piece.state.chargeReady = false
}

trigger lancer_recover {
  on: turn_start
  when: any Lancer where not state.chargeReady
  do: set piece.state.chargeReady = true
}

Necromancer (강령술사)

잡힌 기물에서 영혼을 모아 좀비를 소환합니다.

chesslang
piece Necromancer {
  move: step(diagonal)
  capture: =move
  state: { souls: 0 }
  value: 6
}

piece Zombie {
  move: step(forward) | step(orthogonal)
  capture: step(any) where enemy
  value: 1
}

trigger collect_soul {
  on: capture
  when: any Necromancer where owner == piece.owner
  do: set Necromancer.state.souls = Necromancer.state.souls + 1
}

trigger raise_zombie {
  on: turn_start
  when: any Necromancer where state.souls >= 3
  do: {
    create Zombie at random_empty_square adjacent to Necromancer
    set Necromancer.state.souls = Necromancer.state.souls - 3
  }
}

Jester (광대)

잡는 대신 대상 기물과 위치를 교환합니다.

chesslang
piece Jester {
  move: step(any)
  capture: none        # 잡기 불가, 교환만 가능
  state: { swapsUsed: 0 }
  value: 4
}

trigger jester_swap {
  on: move
  when: piece.type == Jester and target has piece
  do: {
    let targetPiece = piece_at(target)
    move targetPiece to origin
    set piece.state.swapsUsed = piece.state.swapsUsed + 1
  }
}

Medusa (메두사)

대각선 시야의 적 기물을 동결시킵니다.

chesslang
piece Medusa {
  move: step(any)
  capture: slide(diagonal) where enemy
  state: { gazeActive: true }
  value: 5
}

effect frozen {
  blocks: [all]
  duration: 2
  visual: highlight(cyan, 0.5)
}

trigger medusa_gaze {
  on: turn_start
  when: any Medusa where state.gazeActive
  do: mark pieces in line(diagonal) from Medusa where enemy with frozen
}

Time Bomb (시한폭탄)

3턴 후 폭발하여 주변 기물을 제거합니다.

chesslang
piece TimeBomb {
  move: step(orthogonal)
  capture: none
  state: { timer: 3, armed: true }
  value: 1
}

trigger bomb_countdown {
  on: turn_end
  when: any TimeBomb where state.armed
  do: set piece.state.timer = piece.state.timer - 1
}

trigger bomb_explode {
  on: turn_start
  when: any TimeBomb where state.timer <= 0
  do: {
    remove pieces in radius(2) from TimeBomb where not King
    remove TimeBomb
  }
}

Teleporter (텔레포터)

쿨다운이 있지만 빈 칸 어디로든 순간이동할 수 있습니다.

chesslang
piece Teleporter {
  move: step(orthogonal)           # 일반 이동
  capture: step(orthogonal)
  state: { warpReady: true, warpCooldown: 0 }
  value: 5
}

trigger teleporter_warp {
  on: move
  when: piece.type == Teleporter and piece.state.warpReady and distance > 1
  do: {
    set piece.state.warpReady = false
    set piece.state.warpCooldown = 3
  }
}

trigger warp_cooldown {
  on: turn_end
  when: any Teleporter where state.warpCooldown > 0
  do: {
    set piece.state.warpCooldown = piece.state.warpCooldown - 1
    if piece.state.warpCooldown == 0 {
      set piece.state.warpReady = true
    }
  }
}

모범 사례

  1. 단순하게 시작: 조건을 추가하기 전에 기본 이동 패턴부터 시작
  2. 점진적 테스트: 한 번에 하나의 기능만 추가하고 테스트
  3. 명확한 이름 사용: 기물과 효과의 이름을 설명적으로 작성
  4. 밸런스 고려: 커스텀 기물에 적절한 가치 할당
  5. 동작 문서화: 복잡한 트리거에 주석 추가

다음 단계