Regular Games

Język Ogólnego Opisu Gier Oparty o Automaty Skończone

DUKAI, 12 grudnia 2025

Klasyczne Game Playing

Osobny program dla każdej gry.

General Game Playing

Jeden program dla wszystkich gier.

Dlaczego GGP?

  1. Krok w kierunku silnej sztucznej inteligencji.​
  2. Solidna baza dla uczenia nienadzorowanego.
  3. Brak ograniczeń na złożoność problemów.
  4. Generowanie gier i ich wariantów.
  5. Tematyka przystępna dla studentów.

Niestety, jest to dość niszowa dziedzina.

Regular Boardgames

AAAI 2019

Regular Boardgames

Formalnie, gra jest zadana wyrażeniem regularnym, opisującym wszystkie legalne ciągi ruchów.

Opisywalna klasa gier obejmuje wszystkie skończone turowe gry z pełną informacją. W praktyce mieści się tutaj większość popularnych gry planszowe jak Go czy Szachy, ale nie ma tu gier karcianych jak Poker.

AAAI 2019

Ludii

ECAI 2020

Ludii

Formalnie, gra jest złożona z ludemów, opisującym jakąś część gry, jak pionki czy warunek zakończenia.

ECAI 2020

Opisywalna klasa gier obejmuje wszystkie skończone turowe gry z niepełną informacją. W porównaniu z Regular Boardgames możemy opisać Pokera ale też nowoczesne strategiczne gry planszowe jak Catan czy Monopoly.

System Regular Games

(Język) Regular Games

Formalnie, gra zadana jest automatem skończonym z etykietami na krawędziach, opisującym legalne ciągi ruchów.

Opisywalna klasa gier jest taka sama jak w przypadku Ludii, więc wszystkie skończone turowe gry z niepełną informacją.

Gry z ruchami symultanicznymi można symulować korzystając z niepełnej informacji.

Regular Games

A typedef is a type alias.
It has an identifier and the type itself.

type Piece = {e, b, w};
type Player = {white, black};
type PlayerPiece = Player -> Piece;
type IsOpponentPiece = Player -> Piece -> Bool;

A type is either a set of symbols or a mapping between two types.

Regular Games

A variable stores values of a given type. It always has an initial value.

var playerTurn: Player = X;
var coord: Coord = 0;
var board: Coord -> Piece = {
    v00:b, v10:w,
    v01:w, v11:b,
    :e
};

A value is either a symbol or a map. Every map has a default value.

Regular Games

A constant is just like a variable but cannot be assigned to.

const whiteOrEmpty: PieceToBool = {w:1, e:1, :0};
const blackOrEmpty: PieceToBool = {b:1, e:1, :0};
const opponentOrEmpty: PlayerToPieceToBool = {
    white:blackOrEmpty,
         :whiteOrEmpty
};

That's it 😅

Regular Games

An edge is a label joining two nodes. A node is a symbol.

A label is either a skip, an assignment, a comparison, a reachability, or a tag.

turn, move: ? checkForEmpty -> emptyExists;

move, chooseX: player = PlayerOrSystem(playerTurn);

chooseX, choosenX: posX = Coord(*);

choosenX, chooseY: $$ posX;

check, set: board[posX][posY] == Piece(e);

Regular Games

Both assignments and comparisons consist of two expressions.

An expression is either a reference, a map access, or a type cast

some_constant[symbol] == TypeName(some_variable)

Regular Games

A comparison is legal when both sides are equal.

A skip, assignment, and tag are always legal.

A reachability is (not) legal when the second node is (not) reachable from the first node. All assignments are discarded afterwards.

(Język) Regular Games

Opis gry składa się z: 

  1. Typów, używanych w innych częściach opisu.
  2. Zmiennych, przetrzymujących stan gry.
  3. Stałych, wartości pomocniczych.
  4. Krawędzi, opisujących automat.
  5. Pragm, pomagających interpreterowi i kompilatorowi generować efektywniejszy kod.

Przebieg symulacji

Sam automat służy nam jako tak zwany forward model, pozwalający symulować rozgrywki.

start: () => State







moves: (State) => Move[]







apply: (State, Move) => State








is_terminal: (State) => boolean








get_scores: (State) => Scores

7

4

7

0

-3

-2

Opis ruchu

Ruch zaczyna i kończy się gdy gracz przejmuje kontrolę.

Ruch opisujemy ciągiem tagów, czyli specjalnym rodzajem krawędzi.

Pozostałe krawędzie nie są ważne, ale wszystkie muszą być legalne.

High-level Regular Games

Idea języka jest taka sama jak w Regular Games, ale naszym celem jest łatwość użycia:

  1. Typy można sumować i definiować za pomocą zakresów liczb.
  2. Stałe wspierają za pomocą pattern matching'u.
  3. Mapy opisujemy złożeniami.
  4. Krawędzie nie są nam już potrzebne; od teraz programujemy strukturalnie.

Haskell

Ada

Haskell

Haskell

JavaScript

Inspiracja

High-level Regular Games

Idea języka jest taka sama jak w Regular Games, ale naszym celem jest łatwość użycia:

  1. Typy można sumować i definiować za pomocą zakresów liczb.
  2. Stałe wspierają za pomocą pattern matching'u.
  3. Mapy opisujemy złożeniami.
  4. Krawędzie nie są nam już potrzebne; od teraz programujemy strukturalnie.

High-level Regular Games

domain Piece = empty | x | o
domain Player = x | o
domain Position = P(I, J)
    where I in 0..2, J in 0..2
domain Score = 50 | 0 | 100

High-level Regular Games

next_d2 : Position -> Position
next_d2(P(I, J)) = if I + J == 2
    then P((I + 1) % 3, (J - 1) % 3)
    else P(I, J)

next_h : Position -> Position
next_h(P(I, J)) = P(I, (J + 1) % 3)

High-level Regular Games

board : Position -> Piece = {
    P(I, J) = empty
    where I in 0..2, J in 0..2
}

High-level Regular Games

reusable graph existsNonempty() {
  position = Position(*)
  check(board[position] == empty)
}

High-level Regular Games

graph turn(me: Player) {
  player = me
  position = Position(*)
  check(board[position] == empty)
  board[position] = me
  $$ position

  player = keeper
  if reachable(win()) {
    goals[me] = 100
    goals[op[me]] = 0
    end()
  }

  if not(reachable(existsNonempty())) {
    end()
  }
}
reusable graph win(p: Position) {
  branch {
    check(position != next_d1(position))
    check(board[position] == board[next_d1(position)])
    check(board[position] == board[next_d1(next_d1(position))])
  } or {
    check(board[position] == board[next_v(position)])
    check(board[position] == board[next_v(next_v(position))])
  } or { ... }
}

High-level Regular Games

graph rules() {
  loop {
    turn(x)
    turn(o)
  }
}

Przykład HRG

type Position = {
	V__0_0, V__0_1, ..., V__0_7,
    V__1_0, V__1_1, ..., V__1_7,
    ...,
    V__7_0, V__7_1, ..., V__7_7
};
type PositionOrNull = {
    null,
	V__0_0, V__0_1, ..., V__0_7,
    V__1_0, V__1_1, ..., V__1_7,
    ...,
    V__7_0, V__7_1, ..., V__7_7
};
domain Position = V(X, Y) where X in 0..7, Y in 0..7
domain PositionOrNull = null | Position

HRG

RG

Narzędzia

Narzędzia

Wydajność

We wszystkich grach, Regular Games jest szybsze niż Regular Boardgames i Ludii.

Co więcej, dużo gier napisanych w Regular Boardgames jest szybsza po translacji do Regular Games.

Interpreter dostępny w przeglądarce jest oczywiście wolniejszy, ale wystarczająco szybki do eksperymentowania.

Pytania?

Pytania?

Regular Games (DUKAI)

By Radosław Miernik

Regular Games (DUKAI)

DUKAI (https://dukai.cs.uni.wroc.pl), 2025-12-12.

  • 0