一种更简单的方法来确定井字游戏中的赢家?

时间:2018-04-09 21:52:22

标签: alloy

我用这种方式模拟了井字游戏板:

one sig gameBoard {
    cells: Row -> Col -> Mark -> Time
}

Mark是X或O:

enum Mark { X, O }

Row和Col只是设置:

sig Row {}{ #Row = 3}
sig Col {}{ #Col = 3} 

在以下情况下有胜利者:

  • 有一行包含所有X或所有O,或
  • 有一个所有X或所有O的col,或
  • 从左到右的对角线上有所有X或所有O,或
  • 所有X或所有O都有从右到左的对角线。

我用以下复杂谓词来表达。是否有更简单的方式来表达获胜者?

 pred winner [t: Time] {
    some m: Mark |
        some r: Row | all c: Col | board[r, c, t] = m
        or
        some c: Col| all r: Row | board[r, c, t]  = m
        or
        board[first, first, t] = m and 
        board[first.next, first.next, t] = m and 
        board[first.next.next, first.next.next, t] = m
        or
        board[last,last, t] = m and 
        board[last.prev, last.prev, t] = m and 
        board[last.prev.prev,last.prev.prev, t] = m
}

这是我完整的tic-tac-toe模型:

open util/ordering[Time]
open util/ordering[Row]
open util/ordering[Col]

/*
Structure:
1. The game is played on a 3x3 board.
2. There are two players, Player1 and Player2.
3. Players mark the game board with either X or O.
4. The game is played over a series of time steps.
*/

// 4. The game is played over a series of time steps.
sig Time {}

// 3. Players mark the game board with either X or O.
enum Mark { X, O }

// 2. There are two players, Player1 and Player2.
enum Player { Player1, Player2 }

// 1. The game is played on a ... board.
one sig gameBoard {
    cells: Row -> Col -> Mark -> Time
} 

// 1. ... on a 3x3 board.
sig Row {}{ #Row = 3}
sig Col {}{ #Col = 3}

/*
Constraints:
1. Each cell has at most one mark (X or O) at each time.
2. A win stops further marking.
3. When all cells are marked, there can be no additional marking.
4. Players alternate moves.
5. There is no interrupt in the play: If cells are empty at time t-1,
     and there is no winner at time t-1, then there will be one
     fewer empty cells at time t. If there is a winner at time t-1,
     then there will be no change to the number of empty cells at
     time t (per invariant 2). 
6. Player1 marks cells O and Player2 marks cells X.
7. When there is a winner or when all cells are marked,
     then the recording of "last player to move" is blank.
*/

// 1. Each cell has at most one mark (X or O) at each time.
pred Each_cell_has_at_most_one_mark {
    no r: Row, c: Col, t: Time, disj m, m': Mark |
        ((r -> c -> m) in gameBoard.cells.t) and
       ((r -> c -> m') in gameBoard.cells.t) 
}

// 2. A win stops further marking.
pred gameBoard_remains_unchanged_after_win {
    all t: Time - first | 
        winner[t.prev] => gameBoard.cells.t = gameBoard.cells.(t.prev)
}

// 3. When all cells are marked, there can be no additional marking.
pred gameBoard_remains_unchanged_after_every_cell_is_marked {
    all t: Time - first | 
        every_cell_is_marked[t.prev] => gameBoard.cells.t = gameBoard.cells.(t.prev)
}

// 4. Players alternate moves.
pred Players_alternately_move {
    no t: Time - last, t': t.next | 
        (some LastPlayerToMove.person.t)  and
        (some LastPlayerToMove.person.t') and
        (LastPlayerToMove.person.t = LastPlayerToMove.person.t')
}

// 5. There is no interrupt in the play: If cells are empty at time t-1,
//     and there is no winner at time t-1, then there will be one
//     fewer empty cells at time t. If there is a winner at time t-1,
//     then there will be no change to the number of empty cells at
//     time t (per invariant 2). 
pred Progressively_fewer_empty_cells {
    all t: Time - first | 
        not every_cell_is_marked[t.prev] and not winner[t.prev] =>
            #empty_cells[t] < #empty_cells[t.prev]
}

// 6. Player1 marks cells O and Player2 marks cells X.
pred Players_mark_cells_appropriately {
    all t: Time - first |
        not every_cell_is_marked[t.prev] and not winner[t.prev] =>
            let c = gameBoard.cells.t - gameBoard.cells.(t.prev) |
                c[Row][Col] = X =>
                   (LastPlayerToMove.person.t = Player2)
                else
                   (LastPlayerToMove.person.t = Player1)
}

// 7. When there is a winner or when all cells are marked,
//      then the recording of "last player to move" is blank.
pred LastPlayerToMove_remains_unchanged_after_win_or_all_cells_marked {
    all t: Time - first |
        ((every_cell_is_marked[t.prev]) or (winner[t.prev])) => 
            no LastPlayerToMove.person.t 
}

// This provides one place that you can call to 
// have all the constraints enforced.
pred game_is_constrained_by_these_constraints {
    Each_cell_has_at_most_one_mark
    gameBoard_remains_unchanged_after_win
    gameBoard_remains_unchanged_after_every_cell_is_marked
    Players_alternately_move
    Progressively_fewer_empty_cells
    Players_mark_cells_appropriately
    LastPlayerToMove_remains_unchanged_after_win_or_all_cells_marked
}

// Return the set of empty cells at time t.
// This is implemented using set subtraction.
// (Row -> Col) is the set of all possible combinations
// of row and col. Subtract from that the set
// of (row, col) pairs containing a mark at time t.
fun empty_cells[t: Time]: Row -> Col {
    (Row -> Col) - gameBoard.cells.t.Mark
}

// Once the game board is completely marked,
// there won't be a "last player." Ditto for when
// there is a winner. That's why there "may" be
// a last player at time t. That is, there isn’t 
// necessarily a player involved at every time step, 
// i.e., there isn’t necessarily a (Player, Time) pair 
// for every value of Time.
one sig LastPlayerToMove {
    person: Player lone -> Time
}

// Return the mark (X or O) on board[r][c] at time t,
// or none if there is no mark.
fun board [r: Row, c: Col, t: Time]: lone Mark {
    gameBoard.cells[r][c].t
}

// There is a winner when (a) there is a row
// with all X's or all O's, or (b) there is a col
// with all X's or all O's, or (c) there is a left-to-right
// diagonal with all X's or all O's, or (d) there is a 
// right-to-left diagonal with all X's or all O's.
pred winner [t: Time] {
    some m: Mark |
        some r: Row | all c: Col | board[r, c, t] = m
        or
        some c: Col| all r: Row | board[r, c, t]  = m
        or
        board[first, first, t] = m and 
        board[first.next, first.next, t] = m and 
        board[first.next.next, first.next.next, t] = m
        or
        board[last,last, t] = m and 
        board[last.prev, last.prev, t] = m and 
        board[last.prev.prev,last.prev.prev, t] = m
}

// Every call of the game board is marked when
// the set of cells with marks equals all combinations
// of (row, col)
pred every_cell_is_marked[t: Time] {
    gameBoard.cells.t.Mark = (Row -> Col)
}

// Initially the game board has no cells.
// One of the players is first to play.
// The game is constrained by the invariants.
pred init [t: Time] {
    no gameBoard.cells.t 
    one p: Player | LastPlayerToMove.person.t = p
    game_is_constrained_by_these_constraints
}

pred doNothing [t: Time] {
    gameBoard.cells.t = gameBoard.cells.(t.prev)
}

pred Play {
    init[first]
    all t: Time - first |
        X.marked_on_gameboard_at_time[t] 
        or O.marked_on_gameboard_at_time[t]
        or doNothing[t]
}

pred marked_on_gameboard_at_time [m: Mark, t: Time] {
    some r: Row, c: Col {
        gameBoard.cells.t = gameBoard.cells.(t.prev) + 
              {r': Row, c': Col, m': Mark | r' = r and c' = c and m' = m}
    }
}

run Play for 3 but 12 Time

2 个答案:

答案 0 :(得分:1)

评论:您可以在Alloy 5 Beta

中运行整个降价文本

井字棋

我们围绕 Board 设计这款游戏。董事会是州,我们将使用谓词编码的游戏规则 限制转换到下一个板。

设置

通过定义棋盘大小,棋盘和玩家来设置游戏。董事会的人数相对较多 因为这使得跟踪输出很好阅读。

```合金

open util/ordering[Board]

let N = 0+1+2
let highest     =   max[N]



sig Board {
    cells : N -> N -> Cell,
    move: Cell,
    to  : N->N,
    won : Cell
}

enum Cell { _, X, O }

```

获奖

当有一个行,一个列或一个持有相同玩家的对角线时,游戏就会胜出。我们可以按如下方式编码:

```合金

let rightdiag   =   { r,c : N | r = c }
let leftdiag    =   { r,c : N | c = highest.minus[r] }

pred won[b,b':Board ] {
    some token : X + O {
        let positions = b.cells.token {

                some row    : N | N = row.positions 
            or  some column : N | N = positions.column 
            or  rightdiag in positions
            or  leftdiag in positions

            b'.won = token
        }
    }
}

```

完成

当玩家获胜或没有更多免费名额时,游戏结束。

```合金

pred finished[ b,b' : Board ] {
    not won[b,b'] implies {
        b'.won = _
        _ not in b'.cells[N][N]
    }
    b'.cells = b.cells
    b'.move = _
    b'.to = none -> none
}

```

播放

比赛应该在球员之间交替进行。这就是我们将玩家留在前一个棋盘move字段的原因 并检查它不是我们。然后我们约束董事会将一个空位置替换为玩家的位置 令牌。

```合金

pred play[b, b' : Board ] {
    b'.won=_
    some token : X+O {
        b.move != token
        b'.move = token
        some row,col : N {
            b.cells[row][col] = _
            b'.cells = b.cells - row->col->_ + row->col->token
            b'.to = row->col
        }
    }
}

```

微量

然后唯一剩下的就是设置第一块板并确保游戏( Boards 的痕迹)是 根据规则进行比赛。

```合金

fact trace {

    first.move = _
    first.won = _
    first.cells = N->N->_

    all b' : Board - first, b  : b'.prev {
        not finished[b,b'] => play[b,b']
    }
}

```

运行

使用运行,我们可以寻找某些类型的解决方案。在这个例子中,我们试图找到一个O的游戏 以对角线赢得比赛......

```合金

run { some b : Board | rightdiag in b.cells.(O) } for 11 but 3 int

```

这在Alloy 5 Table视图中提供了以下输出(表格从beta 5开始重新编号):

┌──────────┬──────┬────┬───┬───┐
│this/Board│cells │move│to │won│
├──────────┼─┬─┬──┼────┼───┼───┤
│Board⁰    │0│0│_⁰│_⁰  │   │_⁰ │
│          │ ├─┼──┼────┤   ├───┤
│          │ │1│_⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │2│_⁰│    │   │   │
│          ├─┼─┼──┤    │   │   │
│          │1│0│_⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │1│_⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │2│_⁰│    │   │   │
│          ├─┼─┼──┤    │   │   │
│          │2│0│_⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │1│_⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │2│_⁰│    │   │   │
├──────────┼─┼─┼──┼────┼─┬─┼───┤
│Board¹    │0│0│_⁰│X⁰  │2│1│_⁰ │
│          │ ├─┼──┼────┼─┴─┼───┤
│          │ │1│_⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │2│_⁰│    │   │   │
│          ├─┼─┼──┤    │   │   │
│          │1│0│_⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │1│_⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │2│_⁰│    │   │   │
│          ├─┼─┼──┤    │   │   │
│          │2│0│_⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │1│X⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │2│_⁰│    │   │   │
├──────────┼─┼─┼──┼────┼─┬─┼───┤
│Board²    │0│0│_⁰│O⁰  │1│2│_⁰ │
│          │ ├─┼──┼────┼─┴─┼───┤
│          │ │1│_⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │2│_⁰│    │   │   │
│          ├─┼─┼──┤    │   │   │
│          │1│0│_⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │1│_⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │2│O⁰│    │   │   │
│          ├─┼─┼──┤    │   │   │
│          │2│0│_⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │1│X⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │2│_⁰│    │   │   │
├──────────┼─┼─┼──┼────┼─┬─┼───┤
│Board³    │0│0│_⁰│X⁰  │0│1│_⁰ │
│          │ ├─┼──┼────┼─┴─┼───┤
│          │ │1│X⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │2│_⁰│    │   │   │
│          ├─┼─┼──┤    │   │   │
│          │1│0│_⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │1│_⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │2│O⁰│    │   │   │
│          ├─┼─┼──┤    │   │   │
│          │2│0│_⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │1│X⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │2│_⁰│    │   │   │
├──────────┼─┼─┼──┼────┼─┬─┼───┤
│Board⁴    │0│0│O⁰│O⁰  │0│0│_⁰ │
│          │ ├─┼──┼────┼─┴─┼───┤
│          │ │1│X⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │2│_⁰│    │   │   │
│          ├─┼─┼──┤    │   │   │
│          │1│0│_⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │1│_⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │2│O⁰│    │   │   │
│          ├─┼─┼──┤    │   │   │
│          │2│0│_⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │1│X⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │2│_⁰│    │   │   │
├──────────┼─┼─┼──┼────┼─┬─┼───┤
│Board⁵    │0│0│O⁰│X⁰  │2│0│_⁰ │
│          │ ├─┼──┼────┼─┴─┼───┤
│          │ │1│X⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │2│_⁰│    │   │   │
│          ├─┼─┼──┤    │   │   │
│          │1│0│_⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │1│_⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │2│O⁰│    │   │   │
│          ├─┼─┼──┤    │   │   │
│          │2│0│X⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │1│X⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │2│_⁰│    │   │   │
├──────────┼─┼─┼──┼────┼─┬─┼───┤
│Board⁶    │0│0│O⁰│O⁰  │1│1│_⁰ │
│          │ ├─┼──┼────┼─┴─┼───┤
│          │ │1│X⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │2│_⁰│    │   │   │
│          ├─┼─┼──┤    │   │   │
│          │1│0│_⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │1│O⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │2│O⁰│    │   │   │
│          ├─┼─┼──┤    │   │   │
│          │2│0│X⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │1│X⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │2│_⁰│    │   │   │
├──────────┼─┼─┼──┼────┼─┬─┼───┤
│Board⁷    │0│0│O⁰│X⁰  │1│0│_⁰ │
│          │ ├─┼──┼────┼─┴─┼───┤
│          │ │1│X⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │2│_⁰│    │   │   │
│          ├─┼─┼──┤    │   │   │
│          │1│0│X⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │1│O⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │2│O⁰│    │   │   │
│          ├─┼─┼──┤    │   │   │
│          │2│0│X⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │1│X⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │2│_⁰│    │   │   │
├──────────┼─┼─┼──┼────┼─┬─┼───┤
│Board⁸    │0│0│O⁰│O⁰  │2│2│_⁰ │
│          │ ├─┼──┼────┼─┴─┼───┤
│          │ │1│X⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │2│_⁰│    │   │   │
│          ├─┼─┼──┤    │   │   │
│          │1│0│X⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │1│O⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │2│O⁰│    │   │   │
│          ├─┼─┼──┤    │   │   │
│          │2│0│X⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │1│X⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │2│O⁰│    │   │   │
├──────────┼─┼─┼──┼────┼───┼───┤
│Board⁹    │0│0│O⁰│_⁰  │   │O⁰ │
│          │ ├─┼──┼────┤   ├───┤
│          │ │1│X⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │2│_⁰│    │   │   │
│          ├─┼─┼──┤    │   │   │
│          │1│0│X⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │1│O⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │2│O⁰│    │   │   │
│          ├─┼─┼──┤    │   │   │
│          │2│0│X⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │1│X⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │2│O⁰│    │   │   │
├──────────┼─┼─┼──┼────┼───┼───┤
│Board¹⁰   │0│0│O⁰│_⁰  │   │O⁰ │
│          │ ├─┼──┼────┤   ├───┤
│          │ │1│X⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │2│_⁰│    │   │   │
│          ├─┼─┼──┤    │   │   │
│          │1│0│X⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │1│O⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │2│O⁰│    │   │   │
│          ├─┼─┼──┤    │   │   │
│          │2│0│X⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │1│X⁰│    │   │   │
│          │ ├─┼──┤    │   │   │
│          │ │2│O⁰│    │   │   │
└──────────┴─┴─┴──┴────┴───┴───┘

答案 1 :(得分:1)

我在Lightning中实现这个例子很有趣。您可能会发现我的结果很有趣:

enter image description here

Lightning()是一个基于Alloy的语言工作台,允许您为任何Alloy规范定义具体语法。具体语法是通过使用名为的专用模型转换语言(Alloy变体具有可解释性而非可分析性)来定义的,因此您可能需要一些时间来适应它。

该工具可用作eclipse插件(update site)。

此处的an archive file包含上图所示的TicTacToe项目的来源,以便您可以自己玩这个例子。