当前我正在用android创建井字游戏
我已经成功创建了游戏,但遇到了一些问题
这是到目前为止我尝试过的代码
这是我的BoardView
public class BoardView extends View implements GestureDetector.OnGestureListener, ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener {
private static final int STROKE_WIDTH = 10;
private static final int SWEEPER_WIDTH = 20;
private float[] gridLinePoints;
private Paint gridPaint;
private PointF[][] centerPoints;
private Paint signPaint;
private List<SignData> signDataList;
private @Constants.WinLinePosition int winLinePosition;
private Paint winLinePaint;
private GestureDetector clickDetector;
private OnBoardInteractionListener onBoardInteractionListener;
private ValueAnimator clickAnimator;
private ValueAnimator winLineAnimator;
private ValueAnimator resetAnimator;
private float signRadius;
private float winLineLength;
private float sweeperStartPosition;
private Paint sweeperPaint;
private int[] sweeperColors;
private float[] sweeperStops;
public BoardView(Context context) {
super(context);
init();
}
public BoardView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public BoardView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
@TargetApi(Build.VERSION_CODES.M)
private void init() {
gridLinePoints = new float[16];
centerPoints = new PointF[3][3];
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
centerPoints[i][j] = new PointF();
}
}
signDataList = new ArrayList<>();
winLinePosition = Constants.NONE;
gridPaint = new Paint();
gridPaint.setColor(getContext().getResources().getColor(R.color.holo_green_dark, null));
gridPaint.setAntiAlias(true);
gridPaint.setStrokeWidth(dpToPx(STROKE_WIDTH));
gridPaint.setStrokeCap(Paint.Cap.ROUND);
signPaint = new Paint();
signPaint.setColor(getContext().getResources().getColor(R.color.holo_orange_dark, null));
signPaint.setAntiAlias(true);
signPaint.setStyle(Paint.Style.STROKE);
signPaint.setStrokeWidth(dpToPx(STROKE_WIDTH));
signPaint.setStrokeCap(Paint.Cap.ROUND);
winLinePaint = new Paint();
winLinePaint.setColor(getContext().getResources().getColor(R.color.holo_red_dark, null));
winLinePaint.setAntiAlias(true);
winLinePaint.setStrokeWidth(dpToPx(STROKE_WIDTH));
winLinePaint.setStrokeCap(Paint.Cap.ROUND);
clickDetector = new GestureDetector(getContext(), this);
clickAnimator = new ValueAnimator();
clickAnimator.setDuration(150);
clickAnimator.setInterpolator(new DecelerateInterpolator());
clickAnimator.addUpdateListener(this);
clickAnimator.addListener(this);
winLineAnimator = new ValueAnimator();
winLineAnimator.setDuration(150);
winLineAnimator.setInterpolator(new DecelerateInterpolator());
winLineAnimator.addUpdateListener(this);
winLineAnimator.addListener(this);
resetAnimator = new ValueAnimator();
resetAnimator.setDuration(500);
resetAnimator.setInterpolator(new AccelerateInterpolator());
resetAnimator.addUpdateListener(this);
resetAnimator.addListener(this);
sweeperPaint = new Paint();
sweeperPaint.setAntiAlias(true);
sweeperPaint.setStyle(Paint.Style.FILL);
sweeperColors = new int[3];
sweeperColors[0] = Color.parseColor("#0000DDFF");
sweeperColors[1] = Color.parseColor("#FF00DDFF");
sweeperColors[2] = Color.parseColor("#0000DDFF");
sweeperStops = new float[3];
sweeperStops[0] = 0;
sweeperStops[1] = 0.5f;
sweeperStops[2] = 1;
setLayerType(LAYER_TYPE_SOFTWARE, sweeperPaint);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
getLayoutParams().height = getMeasuredWidth();
setGridLinePoints();
setCenterPoints();
setAnimationValues();
}
@Override
protected void onDraw(Canvas canvas) {
drawGrid(canvas);
super.onDraw(canvas);
if (resetAnimator.isRunning()) {
canvas.clipRect(0, sweeperStartPosition, getMeasuredWidth(), getMeasuredWidth());
setSweeperGradient();
canvas.drawRect(0, sweeperStartPosition, getMeasuredWidth(), sweeperStartPosition + dpToPx(SWEEPER_WIDTH), sweeperPaint);
}
drawSigns(canvas);
drawWinLine(canvas);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if ((!isEnabled()) || (clickAnimator.isRunning()) || (isAnimationFlagSet())) {
return super.onTouchEvent(event);
} else {
return clickDetector.onTouchEvent(event);
}
}
private boolean isAnimationFlagSet() {
for (SignData signData : signDataList) {
if (signData.isAnimationFlag()) {
return true;
}
}
return false;
}
private void setGridLinePoints() {
int side = getMeasuredWidth();
float padding = dpToPx(STROKE_WIDTH / 2f);
gridLinePoints[0] = gridLinePoints[4] = gridLinePoints[9] = gridLinePoints[13] = padding;
gridLinePoints[1] = gridLinePoints[3] = gridLinePoints[8] = gridLinePoints[10] = side / 3f;
gridLinePoints[2] = gridLinePoints[6] = gridLinePoints[11] = gridLinePoints[15] = side - padding;
gridLinePoints[5] = gridLinePoints[7] = gridLinePoints[12] = gridLinePoints[14] = (2 * side) / 3f;
}
private void setCenterPoints() {
float a = getMeasuredWidth() / 6f;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
centerPoints[i][j].x = a + (j * (2 * a));
centerPoints[i][j].y = a + (i * (2 * a));
}
}
}
private void setAnimationValues() {
clickAnimator.setFloatValues(0, (getMeasuredWidth() / 6f) - dpToPx(2 * STROKE_WIDTH));
winLineAnimator.setFloatValues(0, getMeasuredWidth());
resetAnimator.setFloatValues(-dpToPx(SWEEPER_WIDTH), getMeasuredWidth());
}
private void setSweeperGradient() {
float axis = sweeperStartPosition + (dpToPx(SWEEPER_WIDTH / 2f));
LinearGradient horizontalGradient = new LinearGradient(0, axis, getMeasuredWidth(), axis,
sweeperColors, sweeperStops, Shader.TileMode.CLAMP);
LinearGradient verticalGradient = new LinearGradient(getMeasuredWidth() / 2f, sweeperStartPosition,
getMeasuredWidth() / 2f, sweeperStartPosition + dpToPx(SWEEPER_WIDTH), sweeperColors, sweeperStops,
Shader.TileMode.CLAMP);
ComposeShader shader = new ComposeShader(horizontalGradient, verticalGradient, PorterDuff.Mode.MULTIPLY);
sweeperPaint.setShader(shader);
}
private void drawGrid(Canvas canvas) {
canvas.drawLines(gridLinePoints, gridPaint);
}
private void drawSigns(Canvas canvas) {
for (int i = 0; i < signDataList.size(); i++) {
SignData signData = signDataList.get(i);
switch (signData.getSign()) {
case Constants.CIRCLE:
drawCircle(canvas, centerPoints[signData.getRow()][signData.getColumn()], signData.isAnimationFlag());
break;
case Constants.CROSS:
drawCross(canvas, centerPoints[signData.getRow()][signData.getColumn()], signData.isAnimationFlag());
break;
case Constants.EMPTY:
break;
}
}
}
private void drawCircle(Canvas canvas, PointF center, boolean animationFlag) {
float radius = animationFlag ? signRadius : (getMeasuredWidth() / 6f) - dpToPx(2 * STROKE_WIDTH);
canvas.drawCircle(center.x, center.y, radius, signPaint);
}
private void drawCross(Canvas canvas, PointF center, boolean animationFlag) {
float radius = animationFlag ? signRadius : (getMeasuredWidth() / 6f) - dpToPx(2 * STROKE_WIDTH);
canvas.drawLine(center.x - radius, center.y - radius, center.x + radius, center.y + radius, signPaint);
canvas.drawLine(center.x - radius, center.y + radius, center.x + radius, center.y - radius, signPaint);
}
private void drawWinLine(Canvas canvas) {
float length = winLineLength;
float a = getMeasuredWidth() / 6f;
float padding = dpToPx(STROKE_WIDTH);
switch (winLinePosition) {
case Constants.NONE:
break;
case Constants.ROW_1:
canvas.drawLine(padding, a, length - padding, a, winLinePaint);
break;
case Constants.ROW_2:
canvas.drawLine(padding, a + (2 * a), length - padding, a + (2 * a), winLinePaint);
break;
case Constants.ROW_3:
canvas.drawLine(padding, a + (4 * a), length - padding, a + (4 * a), winLinePaint);
break;
case Constants.COLUMN_1:
canvas.drawLine(a, padding, a, length - padding, winLinePaint);
break;
case Constants.COLUMN_2:
canvas.drawLine(a + (2 * a), padding, a + (2 * a), length - padding, winLinePaint);
break;
case Constants.COLUMN_3:
canvas.drawLine(a + (4 * a), padding, a + (4 * a), length - padding, winLinePaint);
break;
case Constants.DIAGONAL_1:
canvas.drawLine(padding, padding, length - padding, length - padding, winLinePaint);
break;
case Constants.DIAGONAL_2:
canvas.drawLine(getMeasuredWidth() - padding, padding, padding + getMeasuredWidth()
- length, length - padding, winLinePaint);
break;
}
}
void addSignToBoard(@Constants.Sign int sign, int row, int column) {
SignData signData = new SignData();
signData.setSign(sign);
signData.setRow(row);
signData.setColumn(column);
signData.setAnimationFlag(true);
if (clickAnimator.isRunning()) {
clickAnimator.end();
}
signDataList.add(signData);
clickAnimator.start();
}
void showWinLine(@Constants.WinLinePosition int winLinePosition) {
this.winLinePosition = winLinePosition;
winLineAnimator.start();
}
void resetBoard() {
if (!resetAnimator.isRunning()) {
resetAnimator.start();
}
}
boolean isAlreadyAdded(int row, int column) {
for (int i = 0; i < signDataList.size(); i++) {
SignData signData = signDataList.get(i);
if ((signData.getRow() == row) && (signData.getColumn() == column)) {
return true;
}
}
return false;
}
private float dpToPx(float dp) {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getContext().getResources().getDisplayMetrics());
}
@Override
public boolean onDown(MotionEvent e) {
return true;
}
@Override
public void onShowPress(MotionEvent e) {
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
float x = e.getX();
float y = e.getY();
int row = detectIndexOfPartition(y);
int column = detectIndexOfPartition(x);
if ((row != -1) && (column != -1)) {
onBoardInteractionListener.onBoardClick(BoardView.this, row, column);
}
return true;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return false;
}
@Override
public void onLongPress(MotionEvent e) {
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
return false;
}
private int detectIndexOfPartition(float value) {
float maxValue = getMeasuredWidth();
float totalNumberOfPartitions = 3;
float lengthOfSinglePartition = maxValue / totalNumberOfPartitions;
return (int) (value / lengthOfSinglePartition);
}
public void setOnBoardInteractionListener(OnBoardInteractionListener onBoardInteractionListener) {
this.onBoardInteractionListener = onBoardInteractionListener;
}
@Override
public void onAnimationUpdate(ValueAnimator animation) {
if (animation == clickAnimator) {
signRadius = (float) animation.getAnimatedValue();
} else if (animation == winLineAnimator) {
winLineLength = (float) animation.getAnimatedValue();
} else if (animation == resetAnimator) {
sweeperStartPosition = (float) animation.getAnimatedValue();
}
invalidate();
}
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
if (animation == clickAnimator) {
SignData signData = signDataList.get(signDataList.size() - 1);
signData.setAnimationFlag(false);
onBoardInteractionListener.onSignAdded(signData.getSign(), signData.getRow(), signData.getColumn());
signRadius = 0;
} else if (animation == resetAnimator) {
signDataList.clear();
winLinePosition = Constants.NONE;
onBoardInteractionListener.onBoardReset();
}
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
interface OnBoardInteractionListener {
void onBoardClick(BoardView board, int row, int column);
void onSignAdded(@Constants.Sign int sign, int row, int column);
void onBoardReset();
}
private class SignData {
private @Constants.Sign int sign;
private int row;
private int column;
private boolean animationFlag;
@Constants.Sign int getSign() {
return sign;
}
void setSign(@Constants.Sign int sign) {
this.sign = sign;
}
int getRow() {
return row;
}
void setRow(int row) {
this.row = row;
}
int getColumn() {
return column;
}
void setColumn(int column) {
this.column = column;
}
boolean isAnimationFlag() {
return animationFlag;
}
void setAnimationFlag(boolean animationFlag) {
this.animationFlag = animationFlag;
}
}
}
我的大脑课
class Brain {
private static Brain INSTANCE;
private @Constants.Sign
int[][] board = new int[3][3];
private int rowOfResult;
private int columnOfResult;
private int depth;
private @Constants.Sign
int computerSign;
private @Constants.Sign
int playerSign;
private OnProcessCompleteListener onProcessCompleteListener;
private static final int HORIZONTAL = 0;
private static final int VERTICAL = 1;
private static final int DIAGONAL = 2;
@IntDef({HORIZONTAL, VERTICAL, DIAGONAL})
@interface DirectionOfWinLine {
}
// References used by isWin function.
private int[] winSequence = new int[3];
private int[] row = new int[3];
private int[] column = new int[3];
private int[] diagonal1 = new int[3];
private int[] diagonal2 = new int[3];
private Brain() {
}
static Brain getInstance() {
if (INSTANCE == null) {
INSTANCE = new Brain();
}
return INSTANCE;
}
void play() {
if (onProcessCompleteListener == null) {
return;
}
calculateNextMove(computerSign, depth);
onProcessCompleteListener.onNextMoveCalculated(rowOfResult, columnOfResult);
}
private int calculateNextMove(@Constants.Sign int sign, int depth) {
if (isWin(computerSign, false)) {
return 10 - depth;
} else if (isWin(playerSign, false)) {
return depth - 10;
}
if (depth >= 9) {
return 0;
}
List<Integer> scores = new ArrayList<>(), rowIndices = new ArrayList<>(), columnIndices = new ArrayList<>();
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (board[i][j] == Constants.EMPTY) {
board[i][j] = sign;
scores.add(calculateNextMove(getOppositeSign(sign), depth + 1));
rowIndices.add(i);
columnIndices.add(j);
board[i][j] = Constants.EMPTY;
}
}
}
if (sign == computerSign) {
int maxScore = -100;
for (int i = 0; i < scores.size(); i++) {
if (scores.get(i) > maxScore) {
maxScore = scores.get(i);
}
}
return randomizeScore(maxScore, scores, rowIndices, columnIndices);
} else {
int minScore = 100;
for (int i = 0; i < scores.size(); i++) {
if (scores.get(i) < minScore) {
minScore = scores.get(i);
}
}
return randomizeScore(minScore, scores, rowIndices, columnIndices);
}
}
private int randomizeScore(int score, List<Integer> scores, List<Integer> rowIndices, List<Integer> columnIndices) {
List<Integer> equalScoreIndices = new ArrayList<>();
for (int i = 0; i < scores.size(); i++) {
if (scores.get(i) == score) {
equalScoreIndices.add(i);
}
}
Random rand = new Random();
int randomIndex = equalScoreIndices.get(rand.nextInt(equalScoreIndices.size()));
rowOfResult = rowIndices.get(randomIndex);
columnOfResult = columnIndices.get(randomIndex);
return score;
}
private boolean isWin(@Constants.Sign int sign, boolean notifyWinEnabled) {
for (int i = 0; i < 3; i++) {
winSequence[i] = sign;
}
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (i == j) {
diagonal1[i] = board[i][j];
}
if ((i + j) == 2) {
diagonal2[i] = board[i][j];
}
row[j] = board[i][j];
column[j] = board[j][i];
}
if (isEqual(row, winSequence)) {
if (notifyWinEnabled) {
notifyWin(sign, HORIZONTAL, i + 1);
}
return true;
} else if (isEqual(column, winSequence)) {
if (notifyWinEnabled) {
notifyWin(sign, VERTICAL, i + 1);
}
return true;
}
}
if (isEqual(diagonal1, winSequence)) {
if (notifyWinEnabled) {
notifyWin(sign, DIAGONAL, 1);
}
return true;
} else if (isEqual(diagonal2, winSequence)) {
if (notifyWinEnabled) {
notifyWin(sign, DIAGONAL, 2);
}
return true;
}
return false;
}
private boolean isEqual(int[] x, int[] y) {
for (int i = 0; i < 3; i++) {
if (x[i] != y[i]) {
return false;
}
}
return true;
}
void analyzeBoard() {
if (onProcessCompleteListener == null) {
return;
}
if ((!isWin(Constants.CIRCLE, true)) && (!isWin(Constants.CROSS, true)) && (depth >= 9)) {
onProcessCompleteListener.onGameDraw();
}
}
private void notifyWin(@Constants.Sign int sign, @DirectionOfWinLine int direction, int index) {
if (onProcessCompleteListener == null) {
return;
}
@Constants.WinLinePosition int winLinePosition = Constants.NONE;
switch (direction) {
case HORIZONTAL:
switch (index) {
case 1:
winLinePosition = Constants.ROW_1;
break;
case 2:
winLinePosition = Constants.ROW_2;
break;
case 3:
winLinePosition = Constants.ROW_3;
break;
}
break;
case VERTICAL:
switch (index) {
case 1:
winLinePosition = Constants.COLUMN_1;
break;
case 2:
winLinePosition = Constants.COLUMN_2;
break;
case 3:
winLinePosition = Constants.COLUMN_3;
break;
}
break;
case DIAGONAL:
switch (index) {
case 1:
winLinePosition = Constants.DIAGONAL_1;
break;
case 2:
winLinePosition = Constants.DIAGONAL_2;
break;
}
break;
}
onProcessCompleteListener.onGameWin(sign, winLinePosition);
}
void reset() {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
board[i][j] = Constants.EMPTY;
}
}
depth = 0;
}
void setComputerSign(int computerSign) {
this.computerSign = computerSign;
playerSign = getOppositeSign(computerSign);
}
void updateBoard(@Constants.Sign int sign, int row, int column) {
board[row][column] = sign;
depth++;
}
private @Constants.Sign
int getOppositeSign(@Constants.Sign int sign) {
return sign == Constants.CIRCLE ? Constants.CROSS : Constants.CIRCLE;
}
void setOnProcessCompleteListener(OnProcessCompleteListener onProcessCompleteListener) {
this.onProcessCompleteListener = onProcessCompleteListener;
}
interface OnProcessCompleteListener {
void onNextMoveCalculated(int row, int column);
void onGameWin(@Constants.Sign int sign, @Constants.WinLinePosition int winLinePosition);
void onGameDraw();
}
void destroy() {
INSTANCE = null;
}
}
我已经为此处提供的所有代码创建了github存储库
https://github.com/SuperSaiyanGoku3/MyGame
我在上面的代码中遇到了一些问题
到目前为止,我尝试了一些链接,但无法理解如何创建简单的中级和硬性水平
如果需要更多信息,请告诉我。提前致谢。您的努力将不胜感激。
答案 0 :(得分:4)
实施难度:对于中小型难度的支持,我建议您只是随机使用。您已经实现了HARD难度,因此只需要犯“错误”的难度较小的逻辑即可,并且可以随机实现此错误。
private void calculateNextMoveRandom() {
Random rand = new Random();
int randomRow;
int randomColumn;
while (true) {
randomRow = rand.nextInt(3);
randomColumn = rand.nextInt(3);
if (Constants.EMPTY == board[randomRow][randomColumn]) {
rowOfResult = randomRow;
columnOfResult = randomColumn;
return;
}
}
}
2. 位图标记:您可以使用BitmapFactory.decodeResource()
在屏幕上绘制位图。
private void drawCircle(Canvas canvas, PointF center, boolean animationFlag) {
int iconSize = (int) LayoutUtil.getPixelFromDp(MARKER_SIZE, getContext());
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.android);
Bitmap scaled = Bitmap.createScaledBitmap(bitmap, iconSize, iconSize, true);
canvas.drawBitmap(scaled, center.x - (iconSize >> 1), center.y - (iconSize >> 1), signPaint);
}
3. 随机难度:只需使用随机设置难度即可。
brain.setDifficulty(new Random().nextInt(3));
答案 1 :(得分:0)
如果您使用minimax策略玩井字游戏,则可以有多个开关箱,分别对应游戏中的不同难度级别。最幼稚的方法是在minimax树中放置不同的深度阈值。例如,您可以将minimax游戏树扩展到仅2深度(假设为3 * 3井字游戏)以达到轻松的级别,并说到搜索树的末端获得最高难度级别。您可以根据电路板尺寸和预期的难度级别设置此阈值。
实现难度级别的另一种方法是通过实现不同的启发式函数来计算棋盘得分(对棋盘当前状态的良好程度)。您可以使用启发式函数,根据您在该行/列/对角线中所占据的单元格数量来评估单个行/列/对角线分数。例如,如果占用的单元数为1,则得分= x(x可以是任意数字)。如果占用的单元数为2,则分数= x ^ 2,如果为3,则分数= x ^ 3。然后将所有单独的线分数相乘,以获得每一行,每一列或对角线上的善良程度(或您可以将其视为获胜概率)。您可以为您的对手计算一个相似的分数,然后取一个差值以获得一个棋盘得分。因此,该想法是通过针对每个难度级别设计启发式函数的不同变体来实现评估当前板状态的不同方法。糟糕的启发式功能可以轻松解决,因为它会丢掉很多游戏,而设计合理的启发式游戏会采取不输策略(最终会赢得比赛或吸引游戏)。
为了在每次游戏中随机选择一个难度等级,您可以使用随机数生成器选择一个难度等级。您可以将“ 0”分配为简单,将“ 1”分配为中等,将“ 2”分配为困难。您可以使用以下java函数生成介于0和(n-1)之间的整数
int random = Random.nextInt(n)
答案 2 :(得分:0)
Complete Tic Tac Toe with "n x n matrix"
基本思想是,我们只需要检查列/行/对角线元素是否相同。 C#编写的代码,具有基本的单元测试。