Skip to content

Instantly share code, notes, and snippets.

@zephyrtronium
Created January 17, 2020 02:36
Show Gist options
  • Select an option

  • Save zephyrtronium/396103bfc0a8faa1ebda6b97632ac2c8 to your computer and use it in GitHub Desktop.

Select an option

Save zephyrtronium/396103bfc0a8faa1ebda6b97632ac2c8 to your computer and use it in GitHub Desktop.
9by9
------------------------------------------------------------------------
Nine by Nine
A strategic tic tac toe variant by Branden Brown.
------------------------------------------------------------------------
The Nine by Nine playing area contains a three by three board of three
by three grids of cells. Each move within the grid determines the board
in which the next player may move:
1. If possible, the next player moves in the grid with the same intra-
board coordinates as the intra-grid coordinates of the current move.
2. If the grid determined by the above rule is full, the next player
may move anywhere.
3. A player may not claim a cell with the same intra-board coordinates
as its board's intra-grid coordinates on his or her first turn.
4. The first player may move in any cell except those prohibited by
the above rule.
The game does not end until every cell is claimed. When this occurs, the
player with the most lines of three wins.
import java.io.Serializable;
/**
* 3x3 board.
*
* Branden Brown
* 5-12-11
*/
public class Board implements Serializable
{
private Cell[][] grid;
public Board()
{
grid = new Cell[3][3];
for (Cell[] row: grid)
{
row[0] = new Cell();
row[1] = new Cell();
row[2] = new Cell();
}
}
public Board(Cell[][] init)
{
grid = init;
}
public int countLines(Player p, int r, int c)
{
int n = 0;
// test row
if (grid[r][0].equals(p) && grid[r][1].equals(p) && grid[r][2].equals(p))
n++;
// test column
if (grid[0][c].equals(p) && grid[1][c].equals(p) && grid[2][c].equals(p))
n++;
// test diagonals
if (r == c && grid[0][0].equals(p) && grid[1][1].equals(p) && grid[2][2].equals(p))
n++;
if (r == 2 - c && grid[0][2].equals(p) && grid[1][1].equals(p) && grid[2][0].equals(p))
n++;
return n;
}
public int setAt(Player p, int r, int c)
{
grid[r][c].setPlayer(p);
return countLines(p, r, c);
}
public Cell getAt(int r, int c)
{
return grid[r][c];
}
public boolean filled()
{
return grid[0][0].claimed() && grid[0][1].claimed() && grid[0][2].claimed() &&
grid[1][0].claimed() && grid[1][1].claimed() && grid[1][2].claimed() &&
grid[2][0].claimed() && grid[2][1].claimed() && grid[2][2].claimed();
}
}
import java.lang.String;
import java.io.Serializable;
/**
* Describe a single cell.
*
* Branden Brown
* 5-11-11
*/
public class Cell implements Serializable
{
private Player player;
public Cell()
{
player = new Player(-1);
}
public Cell(Player init)
{
player = init;
}
public Player getPlayer()
{
return player;
}
public void setPlayer(Player p)
{
player = p;
}
public boolean claimed()
{
return player.getID() >= 0;
}
public boolean equals(Player p)
{
return player.equals(p);
}
public String toString()
{
if (claimed())
return player.toString();
return "Unclaimed";
}
}
import javax.swing.*;
import java.awt.*;
/**
* ListCellRenderer which displays only colors.
*
* @author Branden Brown
* @version 6-2-11
*/
public class ColorCellRenderer extends JPanel implements ListCellRenderer
{
public ColorCellRenderer()
{
setPreferredSize(new Dimension(50, 20));
}
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus)
{
setBackground(isSelected ? ((Color) value).darker() : (Color) value);
return this;
}
}
import java.awt.Color;
/**
* Array of colors which may be used by players.
*/
public final class Colors
{
public static final Color[] colors = {
Color.black, Color.blue, Color.cyan, Color.gray,
Color.green, Color.lightGray, Color.magenta, Color.orange, Color.pink,
Color.red, Color.yellow,
new Color(155, 168, 174), new Color(27, 130, 47),
};
}
import java.io.Serializable;
/**
* Represent a two-dimensional coordinate.
*
* Branden Brown
* 5-19-11
*/
public class Coord implements Serializable
{
public final int x, y;
public Coord(int x_, int y_)
{
x = x_;
y = y_;
}
public boolean equals(Coord other)
{
return x == other.x && y == other.y;
}
public String toString()
{
return "(" + (x + 1) + ", " + (y + 1) + ")";
}
}
import java.io.*;
/**
* Run Nine by Nine.
*
* Branden Brown
* 5-13-11
*/
public class Driver
{
public static void main(String[] args)
{
Iface iface;
try
{
ObjectInputStream f = new ObjectInputStream(new FileInputStream("9by9.ser"));
iface = new Iface((Manager) f.readObject());
f.close();
iface.updateAllCells();
}
catch (Exception exc)
{
iface = new Iface(new Manager());
}
iface.pack();
iface.setVisible(true);
}
}
import java.io.Serializable;
/**
* 3x3 grid of 3x3 boards.
*
* Branden Brown
* 5-13-11
*/
public class Grid implements Serializable
{
private Board[][] grid;
public Grid()
{
grid = new Board[3][3];
for (Board[] row: grid)
{
row[0] = new Board();
row[1] = new Board();
row[2] = new Board();
}
}
public Grid(Board[][] init)
{
grid = init;
}
public Board getAt(int r, int c)
{
return grid[r][c];
}
public Cell getAt(int r, int c, int br, int bc)
{
return grid[r][c].getAt(br, bc);
}
public Board getAt(Coord b)
{
return grid[b.x][b.y];
}
public Cell getAt(Coord b, Coord c)
{
return grid[b.x][b.y].getAt(c.x, c.y);
}
public void setAt(Board b, int r, int c)
{
grid[r][c] = b;
}
public int setAt(Player p, int r, int c, int br, int bc)
{
return grid[r][c].setAt(p, br, bc);
}
public void setAt(Board b, Coord c)
{
grid[c.x][c.y] = b;
}
public int setAt(Player p, Coord b, Coord c)
{
return grid[b.x][b.y].setAt(p, c.x, c.y);
}
public boolean filled()
{
return grid[0][0].filled() && grid[0][1].filled() && grid[0][2].filled() &&
grid[1][0].filled() && grid[1][1].filled() && grid[1][2].filled() &&
grid[2][0].filled() && grid[2][1].filled() && grid[2][2].filled();
}
}
import java.awt.*;
import javax.swing.*;
import java.lang.String;
/**
* Special button for Nine by Nine cells.
*
* Branden Brown
* 6-3-11
*/
public class GridButton extends JButton
{
public final Coord board, boardCell;
public Cell cell;
public GridButton(Coord b, Coord c, Cell backend)
{
board = b;
boardCell = c;
cell = backend;
setBackground(Color.white);
setSize(32, 32);
}
public void updateCell()
{
if (cell.claimed())
setBackground(cell.getPlayer().getColor());
}
public void changeCell(Cell newCell)
{
cell = newCell;
setBackground(Color.white);
updateCell();
}
public String toString()
{
return board.toString() + ": " + boardCell.toString();
}
}
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.ArrayList;
/**
* Nine-By-Nine user interface.
*
* Branden Brown
* 5-23-11
*/
public class Iface extends JFrame
{
private Manager game;
private JPanel board;
private JPanel grid;
private JPanel menu;
private JPanel bInfo;
private JPanel[][] boards;
private JPanel pInfo;
private GridButton[][][][] cells;
private JButton restart;
private JComboBox pColor;
private JList pList;
private JScrollPane pListPane;
private JTextField pName;
private JLabel pScore;
private JLabel pTime;
private JLabel bInfoLabel;
private Timer timer;
public Iface(Manager newGame)
{
super("Nine by Nine");
game = newGame;
board = new JPanel(new BorderLayout(8, 8));
grid = new JPanel(new GridLayout(3, 3, 8, 8));
menu = new JPanel(new GridLayout(3, 1, 0, 8));
bInfo = new JPanel();
boards = new JPanel[3][3];
for (JPanel[] row: boards)
{
row[0] = new JPanel(new GridLayout(3, 3));
row[1] = new JPanel(new GridLayout(3, 3));
row[2] = new JPanel(new GridLayout(3, 3));
}
pInfo = new JPanel(new GridLayout(4, 1));
cells = new GridButton[3][3][3][3];
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
for (int k = 0; k < 3; k++)
for (int l = 0; l < 3; l++)
cells[i][j][k][l] = new GridButton(new Coord(i, j), new Coord(k, l), game.getCell(i, j, k, l));
restart = new JButton("Restart");
pColor = new JComboBox(Colors.colors);
pList = new JList(game.getAllPlayers());
pListPane = new JScrollPane(pList);
pName = new JTextField();
pScore = new JLabel();
pTime = new JLabel("00:00");
bInfoLabel = new JLabel("Ready.");
setContentPane(board);
board.add(grid);
board.add(menu, BorderLayout.EAST);
board.add(bInfo, BorderLayout.SOUTH);
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
{
JPanel t = boards[i][j];
grid.add(t);
for (int k = 0; k < 3; k++)
for (int l = 0; l < 3; l++)
t.add(cells[i][j][k][l]);
}
menu.add(restart);
menu.add(pListPane);
menu.add(pInfo);
pInfo.add(pName);
pInfo.add(pColor);
pInfo.add(pScore);
pInfo.add(pTime);
bInfo.add(bInfoLabel);
ActionListener gridButtonListener = new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
runTimer();
if (!game.isFinished())
{
GridButton c = (GridButton) e.getSource();
Player cp = game.getCurrentPlayer();
int n = game.claim(c.board, c.boardCell);
if (n >= 0)
{
if (n == 0)
setInfo(cp.toString() + " claimed " + c.board + ": " + c.boardCell + ".");
else
setInfo(cp.toString() + " claimed " + c.board + ": " + c.boardCell + " and formed " + n + (n == 1 ? " line." : " lines."));
c.updateCell();
updatePlayerInfo();
}
if (game.isFinished())
{
ArrayList<Player> winners = game.determineWinners();
if (winners.size() == game.getNumPlayers())
JOptionPane.showMessageDialog(null, "Tie between all players.");
else if (winners.size() > 1)
JOptionPane.showMessageDialog(null, "Tie between: " + winners);
else
JOptionPane.showMessageDialog(null, "Winner: " + winners.get(0));
setInfo("Game over.");
}
}
}
};
for (GridButton[][][] hrow: cells)
for (GridButton[][] brow: hrow)
for (GridButton[] row: brow)
for (GridButton b: row)
b.addActionListener(gridButtonListener);
restart.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
timer.stop();
final Integer[] options = new Integer[Colors.colors.length - 1];
for (int i = 0; i < options.length; i++)
options[i] = new Integer(i + 2);
Integer i = (Integer) JOptionPane.showInputDialog(board, "How many players?", "New Game", JOptionPane.QUESTION_MESSAGE, null, options, options[0]);
if (i == null)
{
runTimer();
return;
}
setGame(new Manager(i.intValue()));
}
});
pList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
pList.addListSelectionListener(new ListSelectionListener()
{
public void valueChanged(ListSelectionEvent e)
{
pList.removeListSelectionListener(this);
updatePlayerInfo();
pList.addListSelectionListener(this);
}
});
pName.getDocument().addDocumentListener(new DocumentListener()
{
private void dowhatiwant()
{
String n = pName.getText();
for (Player p: game.getAllPlayers())
if (n.equals(p.getName()))
return;
game.getCurrentPlayer().setName(n);
}
public void changedUpdate(DocumentEvent e) {}
public void removeUpdate(DocumentEvent e) {dowhatiwant();}
public void insertUpdate(DocumentEvent e) {dowhatiwant();}
});
pColor.setRenderer(new ColorCellRenderer());
pColor.addItemListener(new ItemListener()
{
public void itemStateChanged(ItemEvent e)
{
for (Player p: game.getAllPlayers())
if (p.getColor().equals((Color) e.getItem()))
return;
pColor.setBackground((Color) e.getItem());
game.getCurrentPlayer().setColor((Color) e.getItem());
updateAllCells();
}
});
timer = new Timer(1000 / Player.TIME_SHIFT, new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
Player p = game.getCurrentPlayer();
p.decTime();
pTime.setText(p.getTimeString());
}
});
updatePlayerInfo();
addWindowListener(new WindowAdapter()
{
public void windowClosed(WindowEvent e)
{
try
{
ObjectOutputStream f = new ObjectOutputStream(new FileOutputStream("9by9.ser"));
f.writeObject(game);
f.close();
}
catch (Exception exc) {}
}
});
}
public void updatePlayerInfo()
{
Player p = game.getCurrentPlayer();
pList.setSelectedIndex(p.getID());
pName.setText(p.toString());
pColor.setSelectedItem(p.getColor());
pColor.setBackground(p.getColor());
pScore.setText("" + p.getScore());
pTime.setText(p.getTimeString());
}
public void updateAllCells()
{
for (GridButton[][][] hrow: cells)
for (GridButton[][] brow: hrow)
for (GridButton[] row: brow)
for (GridButton b: row)
b.updateCell();
}
public void setGame(Manager newGame)
{
game = newGame;
pList.setListData(game.getAllPlayers());
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
for (int k = 0; k < 3; k++)
for (int l = 0; l < 3; l++)
cells[i][j][k][l].changeCell(game.getCell(i, j, k, l));
updatePlayerInfo();
}
public void setInfo(String information)
{
bInfoLabel.setText(information);
}
private void runTimer()
{
if (!timer.isRunning() && game.getNumMoves() >= game.getNumPlayers() - 1)
timer.start();
}
}
import java.io.Serializable;
import java.util.ArrayList;
/**
* Manage a game of nine by nine.
*
* Branden Brown
* 5-13-11
*/
public class Manager implements Serializable
{
private Player[] players;
private Grid grid;
private Player currentPlayer;
private Coord prev;
private int numMoves;
public Manager()
{
players = new Player[2];
players[0] = new Player(0);
players[1] = new Player(1);
grid = new Grid();
currentPlayer = players[0];
prev = null;
numMoves = 0;
}
public Manager(int numPlayers)
{
players = new Player[numPlayers];
for (int i = 0; i < numPlayers; i++)
players[i] = new Player(i);
grid = new Grid();
currentPlayer = players[0];
prev = null;
numMoves = 0;
}
public ArrayList<Player> determineWinners()
{
// if (grid.filled())
// {
ArrayList<Player> a = new ArrayList<Player>(players.length);
Player t = new Player(0);
for (Player p: players)
if (p.getScore() > t.getScore())
{
a.clear();
t = p;
a.add(t);
}
else if (p.getScore() == t.getScore())
a.add(p);
return a;
// }
// return null;
}
public int claim(Coord board, Coord cell)
{
if (canClaim(board, cell))
{
int n = grid.setAt(currentPlayer, board, cell);
if (currentPlayer.getTime() > 0)
currentPlayer.addToScore(n);
currentPlayer = players[++numMoves % players.length];
prev = cell;
return n;
}
return -1;
}
public boolean canClaim(Coord board, Coord cell)
{
// return true;
return !(numMoves < players.length && board.equals(cell)) &&
(prev == null || board.equals(prev) || grid.getAt(prev).filled()) &&
!grid.getAt(board, cell).claimed();
}
public boolean isFinished()
{
return numMoves == 81;
}
public Player getCurrentPlayer()
{
return currentPlayer;
}
public int getCurrentPlayerIndex()
{
return numMoves % players.length;
}
public Player getPlayer(int i)
{
return players[i];
}
public Player[] getAllPlayers()
{
return players;
}
public Coord getLastMove()
{
return prev;
}
public int getNumPlayers()
{
return players.length;
}
public int getNumMoves()
{
return numMoves;
}
public Cell getCell(int i, int j, int k, int l)
{
return grid.getAt(i, j, k, l);
}
}
import java.lang.String;
import java.io.Serializable;
import java.awt.Color;
/**
* Describe a player.
*
* Branden Brown
* 5-11-11
*/
public class Player implements Serializable
{
public static final int TIME_SHIFT = 10;
private String name;
private int id;
private int score;
private int time;
private Color color;
public Player(int idno)
{
name = "Player " + (idno + 1);
id = idno;
score = 0;
time = 900 * TIME_SHIFT;
color = id >= 0 ? Colors.colors[id] : Color.white;
}
public Player(String pname, int idno, int lines, int inittime, Color icolor)
{
name = name;
id = idno;
score = lines;
time = inittime;
color = icolor;
}
public String getName()
{
return name;
}
public int getID()
{
return id;
}
public int getScore()
{
return score;
}
public int getTime()
{
return time;
}
public Color getColor()
{
return color;
}
public String getTimeString()
{
int t = time <= 0 ? 0 : time / TIME_SHIFT;
return "" + (t / 60) + ":" + (t % 60 >= 10 ? t % 60 : "0" + t % 60);
}
public void setName(String pname)
{
name = pname;
}
public void addToScore(int n)
{
score += n;
}
public void decTime()
{
time--;
}
public void setColor(Color newColor)
{
color = newColor;
}
public String toString()
{
return name;
}
public boolean equals(Player other)
{
return id == other.getID();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment