Я создаю настольную игру на своем компьютере, используя Java Swing.
Вот изображение Swing GUI. Я понимаю, что на этом изображении трудно прочитать текст квадратов доски. Требование заключается в том, что игрок может двигаться по часовой стрелке или против часовой стрелки на внешней стороне игрового поля и на собрании акционеров.
Когда он пришел Время создать логическую модель игрового поля, я столкнулся с проблемами.
Вот увеличенное изображение в левом нижнем углу игрового поля.
Первой логической моделью, которую я попробовал, был Список моего класса AbstractSquare.
package com.ggl.stockmarket.game.model.board;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import com.ggl.stockmarket.game.model.GameStatus;
import com.ggl.stockmarket.game.view.StockMarketFont;
public abstract class AbstractSquare {
public static final int IMAGE_WIDTH = 160;
public static final int IMAGE_HEIGHT = 192;
public static final Insets INSETS = new Insets(4, 2, 4, 2);
/**
* The key to the square in the bidirectional graph.
*/
protected Integer graphKey;
/**
* The direction to move on the next turn. +1 means clockwise, -1 means
* counter-clockwise, and zero means that an odd dice roll is clockwise, while
* an even dice roll is counter-clockwise.
*/
protected int direction;
/**
* The direction and distance to move the market. A positive integer means the
* market moves down. A negative integer means the market moves up.
*/
protected int marketAmount;
/**
* Pointer to the location in the List of the previous board square.
*/
protected int previousPointer;
/**
* Pointer to the locatio0n in the List of the next board square.
*/
protected int nextPointer;
/**
* The amount to multiply the stock. As an example; "1 for 1" multiplies the
* stock by 2.
*/
protected int stockMultiplier;
protected Color backgroundColor;
protected Dimension squareSize;
protected Rectangle boardLocation;
protected String multiplierText;
public Integer getGraphKey() {
return graphKey;
}
public void setGraphKey(Integer graphKey) {
this.graphKey = graphKey;
}
/**
* The direction to move on the next turn.
*
* @param roll - The dice roll total.
* @return +1 is clockwise and -1 is counter-clockwise.
*/
public int getDirection(int roll) {
if (direction == 0) {
if ((roll / 2 * 2) == roll) {
return -1;
} else {
return +1;
}
} else {
return direction;
}
}
public void setDirection(int direction) {
this.direction = direction;
}
public int getDirection() {
return direction;
}
public int getMarketAmount() {
return marketAmount;
}
public void setMarketAmount(int marketAmount) {
this.marketAmount = marketAmount;
}
public Color getBackgroundColor() {
return backgroundColor;
}
public void setBackgroundColor(Color backgroundColor) {
this.backgroundColor = backgroundColor;
}
public Rectangle getBoardLocation() {
return boardLocation;
}
public void setBoardLocation(Rectangle boardLocation) {
this.boardLocation = boardLocation;
}
public int getPreviousPointer() {
return previousPointer;
}
public void setPreviousPointer(int previousPointer) {
this.previousPointer = previousPointer;
}
public int getNextPointer() {
return nextPointer;
}
public void setNextPointer(int nextPointer) {
this.nextPointer = nextPointer;
}
public int getStockMultiplier() {
return stockMultiplier;
}
public void setStockMultiplier(int stockMultiplier) {
this.stockMultiplier = stockMultiplier;
}
public Dimension getSquareSize() {
return squareSize;
}
public void setSquareSize(Dimension squareSize) {
this.squareSize = squareSize;
}
public String getMultiplierText() {
return multiplierText;
}
public void setMultiplierText(String multiplierText) {
this.multiplierText = multiplierText;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((graphKey == null) ? 0 : graphKey.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
AbstractSquare other = (AbstractSquare) obj;
if (graphKey == null) {
if (other.graphKey != null)
return false;
} else if (!graphKey.equals(other.graphKey))
return false;
return true;
}
public List<String> splitStockName(String name) {
List<String> list = new ArrayList<String>();
int pos = name.lastIndexOf(' ');
if (pos < 0) {
list.add(name);
} else {
list.add(name.substring(0, pos));
list.add(name.substring(pos + 1));
}
return list;
}
public BufferedImage drawOutsideImage() {
int width = squareSize.width;
int height = squareSize.height;
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = (Graphics2D) bufferedImage.getGraphics();
drawOutsideSquare(g, width, height, INSETS);
drawMovementText(g, width, height);
drawMovementArrows(g, width, height);
g.dispose();
return bufferedImage;
}
protected BufferedImage drawOutsideImage(int width, int height, Insets insets) {
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = (Graphics2D) bufferedImage.getGraphics();
drawOutsideSquare(g, width, height, insets);
drawMovementText(g, width, height);
drawMovementArrows(g, width, height);
return bufferedImage;
}
protected void drawOutsideSquare(Graphics2D g, int width, int height, Insets insets) {
g.setColor(Color.black);
g.fillRect(0, 0, width, height);
if (backgroundColor == null) {
g.setColor(Color.white);
} else {
g.setColor(backgroundColor);
}
g.fillRect(insets.left, insets.top, width - insets.right - insets.left, height - insets.bottom - insets.top);
}
protected void drawMovementText(Graphics2D g, int width, int height) {
Font directionFont = StockMarketFont.getBoldFont(16);
FontRenderContext frc = g.getFontRenderContext();
StringBuilder sb = new StringBuilder();
if (marketAmount < 0) {
sb.append("Down ");
sb.append(Math.abs(marketAmount));
} else if (marketAmount > 0) {
sb.append("Up ");
sb.append(marketAmount);
} else {
sb.append("Odd Even");
}
setTextColor(g);
TextLayout layout = new TextLayout(sb.toString(), directionFont, frc);
Rectangle2D bounds = layout.getBounds();
float fx = (float) (bounds.getX()) + (float) (width - bounds.getWidth()) * 0.5F;
float fy = (float) height - 10.0F;
layout.draw(g, fx, fy);
}
public void setTextColor(Graphics2D g) {
g.setColor(Color.black);
if ((backgroundColor != null) && (backgroundColor.equals(Color.blue))) {
g.setColor(Color.white);
}
}
protected void drawMovementArrows(Graphics2D g, int width, int height) {
if (direction == 0) {
int w = (width - 30) / 2;
int x = 10;
int y = height - 40;
drawArrow(g, x, y, +1, w);
x = width - w - 10;
drawArrow(g, x, y, -1, w);
} else {
int w = (width - 40);
int x = (width - w) / 2;
int y = height - 40;
drawArrow(g, x, y, direction, w);
}
}
protected void drawArrow(Graphics2D g, int x, int y, int direction, int length) {
// arrow thickness, arrow point height and width
int t = 4;
int h = 14;
int w = 20;
Polygon p = new Polygon();
if (direction > 0) {
g.fillRect(x + h, y, length - h, t);
p.addPoint(x + h, y - ((w - t) / 2));
p.addPoint(x + h, y + ((w + t) / 2));
p.addPoint(x, y + (t / 2));
} else {
g.fillRect(x, y, length - h, t);
p.addPoint(x + length - h, y - ((w - t) / 2));
p.addPoint(x + length - h, y + ((w + t) / 2));
p.addPoint(x + length, y + (t / 2));
}
g.fillPolygon(p);
}
public BufferedImage drawInsideImage(Insets insets) {
int width = squareSize.width;
int height = squareSize.height;
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = (Graphics2D) bufferedImage.getGraphics();
g.dispose();
return bufferedImage;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append(graphKey);
return builder.toString();
}
public abstract void execute(GameStatus gameStatus);
}
На данный момент игнорируйте поле graphKey. Класс AbstractSquare содержит все поля, необходимые для всех клеток на игровом поле. Ни один квадрат не использует все поля. Каждый квадрат использует поля, необходимые для квадратуры.
Проблема в том, что доска не может быть представлена простым графиком. Как видно на увеличенной игровой доске, направление вашего движения определяется стрелкой внизу квадрата, в котором вы начинаете свой ход. Вы можете двигаться со стартового поля в любом направлении, в зависимости от того, был ли бросок костей нечетным или четным.
Когда вы приземляетесь на площадь встречи стохолдеров во время броска, у вас есть выбор, продолжать ли двигаться по за пределами совета директоров или на собрании акционеров, если у вас есть хотя бы одна акция этой акции.
После того, как я решил, что Список абстрактных квадратов не будет работать, я исследовал ориентированные графы. Я нашел этот код на Geeks For Geeks
// Java program to implement Graph
// with the help of Generics
import java.util.*;
class Graph<T> {
// We use Hashmap to store the edges in the graph
private Map<T, List<T> > map = new HashMap<>();
// This function adds a new vertex to the graph
public void addVertex(T s)
{
map.put(s, new LinkedList<T>());
}
// This function adds the edge
// between source to destination
public void addEdge(T source,
T destination,
boolean bidirectional)
{
if (!map.containsKey(source))
addVertex(source);
if (!map.containsKey(destination))
addVertex(destination);
map.get(source).add(destination);
if (bidirectional == true) {
map.get(destination).add(source);
}
}
// This function gives the count of vertices
public void getVertexCount()
{
System.out.println("The graph has "
+ map.keySet().size()
+ " vertex");
}
// This function gives the count of edges
public void getEdgesCount(boolean bidirection)
{
int count = 0;
for (T v : map.keySet()) {
count += map.get(v).size();
}
if (bidirection == true) {
count = count / 2;
}
System.out.println("The graph has "
+ count
+ " edges.");
}
// This function gives whether
// a vertex is present or not.
public void hasVertex(T s)
{
if (map.containsKey(s)) {
System.out.println("The graph contains "
+ s + " as a vertex.");
}
else {
System.out.println("The graph does not contain "
+ s + " as a vertex.");
}
}
// This function gives whether an edge is present or not.
public void hasEdge(T s, T d)
{
if (map.get(s).contains(d)) {
System.out.println("The graph has an edge between "
+ s + " and " + d + ".");
}
else {
System.out.println("The graph has no edge between "
+ s + " and " + d + ".");
}
}
// Prints the adjancency list of each vertex.
@Override
public String toString()
{
StringBuilder builder = new StringBuilder();
for (T v : map.keySet()) {
builder.append(v.toString() + ": ");
for (T w : map.get(v)) {
builder.append(w.toString() + " ");
}
builder.append("\n");
}
return (builder.toString());
}
}
// Driver Code
public class Main {
public static void main(String args[])
{
// Object of graph is created.
Graph<Integer> g = new Graph<Integer>();
// edges are added.
// Since the graph is bidirectional,
// so boolean bidirectional is passed as true.
g.addEdge(0, 1, true);
g.addEdge(0, 4, true);
g.addEdge(1, 2, true);
g.addEdge(1, 3, true);
g.addEdge(1, 4, true);
g.addEdge(2, 3, true);
g.addEdge(3, 4, true);
// print the graph.
System.out.println("Graph:\n"
+ g.toString());
// gives the no of vertices in the graph.
g.getVertexCount();
// gives the no of edges in the graph.
g.getEdgesCount(true);
// tells whether the edge is present or not.
g.hasEdge(3, 4);
// tells whether vertex is present or not
g.hasVertex(5);
}
}
Проблема с этим кодом заключается в том, что в моих экземплярах AbstractSqare не было естественного ключа, который я мог бы использовать для создания графика игровой доски.
Мой вопрос: как я могу использовать график для реализации логической модели игрового поля?
Мне нужно определить промежуточные квадраты броска костей, чтобы я мог оживить движение кусок. Мне также нужно спросить игрока, участвовать или нет в собрании акционеров, опять же при условии, что игрок владеет хотя бы одной акцией.