In this
post, I finish the Reversi / Othello game in R by improving the graphics, adding
the ability to save and load boards, and fixing bugs. Also, many more boards
have been added and tested, including those with unusual shapes, three or more players,
and walls that can make the board into unusual shapes or even break it in half.
This newer version can be downloaded with this link. It can be run by setting the appropriate working directory and typing source("Reversi 2019-08-07.R").
In this version, stones are represented by coloured circles like they are in most
implementations of Reversi. Spaces were stones can be played are represented by
small grey circles, which become larger when there are legal moves for the current
player.
This is all
done with the plot.stones() function, which has taken over part of the plot.board()
function's work.
plot.stones = function(board, legal_board){
…
First, define
the colours of each stone (or blank space)
Up to six
players can be on the board at once, representing by WURBG and Y. Player turns are
done alphabetically based on the single character representations of the
colour, which is (B)lack, then (G)reen, then (R)ed, then bl(U)e, then (W)hite,
and finally (Y)ellow.
draw_col = rep("",length(chars))
draw_col[which(chars == ".")] = "gray"
draw_col[which(chars == "W")] = "white" #…
draw_col[which(chars == "U")] = "blue"
draw_col[which(chars == "Y")] = "yellow"
Next define
the radii of each circle to be drawn. Recall that each space on the board is drawn
at an integer coordinate, so a radius of 0.35 means that stones should take 70%
of the space between cardinally adjacent spaces. Realistically, it also depends
a bit on the plot window.
draw_rad = rep(0.1,length(chars))
draw_rad[legal] = 0.2
draw_rad[!(chars %in% c(".","
","#"))] = 0.35
For each of
the spaces on the board, draw a circle for each stone or empty space. We do this using the
draw.circle() function from the plotrix package, which takes in the x and y coordinates of the circle center, the radius, border colour, and fill colour. The fill only appears because we specified a colour, otherwise the circle would be transparent.
for(k in 1:length(chars)){
if(draw_col[k] != ""){
draw.circle(x[k],y[k],radius=draw_rad[k],
border="black",col=draw_col[k]) }
For each
wall, draw a square filling the 1x1 space with the base R polygon() function.
We draw nothing
for voids. Mechanically voids and walls are identical, but they are treated differently
graphically.
if(chars[k] == "#"){
size = 0.5
corners_x = c(x[k]-size, x[k]+size, x[k]+size, x[k]-size)
corners_y = c(y[k]-size, y[k]-size, y[k]+size, y[k]+size)
polygon(corners_x,corners_y,col="gray") } }
A few
notable updates to the main function play.game(). At the start we load the required
plotrix function, set the background of any plots to darkgreen, and remove the inner
margins so that we can use the entire plot window. The par() function sets a variety of basic graphical parameters (in the computing science sense, not the statistical one). You can see what the settings available are, and their current value, by simply typing par().
require(plotrix)
par(bg = "darkgreen")
par(mar = c(0,0,0,0))
The while
loop that checks for a valid click also now checks that the plot window is
still open. If it is closed, the loop and the game end. This is done with dev.cur(), which returns the names of any graphical devices, such as plot windows, that are actively. Rstudio and vanilla R have different names for their plotting devices, but both implementations return "null device" if no plot window is open.
while( … &
any(names(dev.cur()) != "null device"))
The import.board()
function takes a raw .txt file and converts in into a matrix of single
characters, which are then used as a board in the game.
The function readLines() takes a text file and saves a vector of string variables, one for each line. The strsplit function splits these into individual characters, but organizes them into a list of vectors, one vector per original string. For these reason, we also need unlist(). The rest is arithmetic.
import.board = function(filename, echo=TRUE)
{
rawboard =
readLines(filename)
rawchars =
unlist(strsplit(rawboard,""))
Ny = length(rawboard)
Nx = length(rawchars)
/ Ny
board =
matrix(rawchars,byrow=TRUE,nrow=Ny,ncol=Nx)
if(echo){print(board)}
return(board)
}
...into this...
...a board with
each of two players already holding two corners.
Among the boards included, and tested, in the download are also:
A corner-filled
version for four players. Shown here is a game in progress for this board.
A diamond-shaped
board with double-size corners. Here a completed game is shown.
Two 6x6 boards
separated by a wall. If a player is eliminated from one side, as black has been on the right, then neither player
can make a move on that side again, so you really have balance priorities.
A setup where
each player has a fortress that extends beyond the usual 8x8 board.
A board on which a few obstacles have been scattered about.
No comments:
Post a Comment