Commit 2b07c7ff authored by Sibidharan's avatar Sibidharan 💬

nitial fork

parents
<?xml version="1.0" encoding="UTF-8"?>
<!-- You may freely edit this file. See commented blocks below for -->
<!-- some examples of how to customize the build. -->
<!-- (If you delete it and reopen the project it will be recreated.) -->
<!-- By default, only the Clean and Build commands use this build script. -->
<!-- Commands such as Run, Debug, and Test only use this build script if -->
<!-- the Compile on Save feature is turned off for the project. -->
<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
<!-- in the project's Project Properties dialog box.-->
<project name="2048" default="default" basedir=".">
<description>Builds, tests, and runs the project 2048.</description>
<import file="nbproject/build-impl.xml"/>
<!--
There exist several targets which are by default empty and which can be
used for execution of your tasks. These targets are usually executed
before and after some main targets. They are:
-pre-init: called before initialization of project properties
-post-init: called after initialization of project properties
-pre-compile: called before javac compilation
-post-compile: called after javac compilation
-pre-compile-single: called before javac compilation of single file
-post-compile-single: called after javac compilation of single file
-pre-compile-test: called before javac compilation of JUnit tests
-post-compile-test: called after javac compilation of JUnit tests
-pre-compile-test-single: called before javac compilation of single JUnit test
-post-compile-test-single: called after javac compilation of single JUunit test
-pre-jar: called before JAR building
-post-jar: called after JAR building
-post-clean: called after cleaning build products
(Targets beginning with '-' are not intended to be called on their own.)
Example of inserting an obfuscator after compilation could look like this:
<target name="-post-compile">
<obfuscate>
<fileset dir="${build.classes.dir}"/>
</obfuscate>
</target>
For list of available properties check the imported
nbproject/build-impl.xml file.
Another way to customize the build is by overriding existing main targets.
The targets of interest are:
-init-macrodef-javac: defines macro for javac compilation
-init-macrodef-junit: defines macro for junit execution
-init-macrodef-debug: defines macro for class debugging
-init-macrodef-java: defines macro for class execution
-do-jar: JAR building
run: execution of project
-javadoc-build: Javadoc generation
test-report: JUnit report generation
An example of overriding the target for project execution could look like this:
<target name="run" depends="2048-impl.jar">
<exec dir="bin" executable="launcher.exe">
<arg file="${dist.jar}"/>
</exec>
</target>
Notice that the overridden target depends on the jar target and not only on
the compile target as the regular run target does. Again, for a list of available
properties which you can use, check the target you are overriding in the
nbproject/build-impl.xml file.
-->
</project>
Manifest-Version: 1.0
X-COMMENT: Main-Class will be added automatically by build
This diff is collapsed.
build.xml.data.CRC32=88356830
build.xml.script.CRC32=0283b34b
build.xml.stylesheet.CRC32=8064a381@1.78.0.48
# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
nbproject/build-impl.xml.data.CRC32=88356830
nbproject/build-impl.xml.script.CRC32=04ccb3e7
nbproject/build-impl.xml.stylesheet.CRC32=2d327b5d@1.78.0.48
compile.on.save=true
user.properties.file=/Users/sibidharan/Library/Application Support/NetBeans/dev-jdk9-branch/build.properties
annotation.processing.enabled=true
annotation.processing.enabled.in.editor=false
annotation.processing.processor.options=
annotation.processing.processors.list=
annotation.processing.run.all.processors=true
annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output
build.classes.dir=${build.dir}/classes
build.classes.excludes=**/*.java,**/*.form
# This directory is removed when the project is cleaned:
build.dir=build
build.generated.dir=${build.dir}/generated
build.generated.sources.dir=${build.dir}/generated-sources
# Only compile against the classpath explicitly listed here:
build.sysclasspath=ignore
build.test.classes.dir=${build.dir}/test/classes
build.test.results.dir=${build.dir}/test/results
# Uncomment to specify the preferred debugger connection transport:
#debug.transport=dt_socket
debug.classpath=\
${run.classpath}
debug.test.classpath=\
${run.test.classpath}
# Files in build.classes.dir which should be excluded from distribution jar
dist.archive.excludes=
# This directory is removed when the project is cleaned:
dist.dir=dist
dist.jar=${dist.dir}/2048.jar
dist.javadoc.dir=${dist.dir}/javadoc
excludes=
includes=**
jar.compress=false
javac.classpath=
# Space-separated list of extra javac options
javac.compilerargs=
javac.deprecation=false
javac.external.vm=true
javac.processorpath=\
${javac.classpath}
javac.source=1.9
javac.target=1.9
javac.test.classpath=\
${javac.classpath}:\
${build.classes.dir}
javac.test.processorpath=\
${javac.test.classpath}
javadoc.additionalparam=
javadoc.author=false
javadoc.encoding=${source.encoding}
javadoc.noindex=false
javadoc.nonavbar=false
javadoc.notree=false
javadoc.private=false
javadoc.splitindex=true
javadoc.use=true
javadoc.version=false
javadoc.windowtitle=
main.class=game.GUI2048
manifest.file=manifest.mf
meta.inf.dir=${src.dir}/META-INF
mkdist.disabled=false
platform.active=default_platform
run.classpath=\
${javac.classpath}:\
${build.classes.dir}
# Space-separated list of JVM arguments used when running the project.
# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value.
# To set system properties for unit tests define test-sys-prop.name=value:
run.jvmargs=
run.test.classpath=\
${javac.test.classpath}:\
${build.test.classes.dir}
source.encoding=UTF-8
src.dir=src
test.src.dir=test
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://www.netbeans.org/ns/project/1">
<type>org.netbeans.modules.java.j2seproject</type>
<configuration>
<data xmlns="http://www.netbeans.org/ns/j2se-project/3">
<name>2048</name>
<source-roots>
<root id="src.dir"/>
</source-roots>
<test-roots>
<root id="test.src.dir"/>
</test-roots>
</data>
</configuration>
</project>
package game;
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
/**
*
* @author sibidharan
*/
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import javax.swing.JPanel;
public class Board extends JPanel {
private static final long serialVersionUID = -1790261785521495991L;
/* Board row and column */
public static final int ROW = 4;
/* this array use for convenience iterate */
public static final int[] _0123 = { 0, 1, 2, 3 };
final GUI2048 host;
private Tile[] tiles;
public static Value GOAL = Value._2048;
public Board(GUI2048 f) {
host = f;
setFocusable(true);
initTiles();
}
/**
* initialize the game, also use to start a new game
*/
public void initTiles() {
tiles = new Tile[ROW * ROW];
for (int i = 0; i < tiles.length; i++) {
tiles[i] = Tile.ZERO;
}
addTile();
addTile();
host.statusBar.setText("");
}
/**
* move all the tiles to the left side.
*/
public void left() {
boolean needAddTile = false;
for (int i : _0123) {
// get i-th line
Tile[] origin = getLine(i);
// get the line have been moved to left
Tile[] afterMove = moveLine(origin);
// get the the line after
Tile[] merged = mergeLine(afterMove);
// set i-th line with the merged line
setLine(i, merged);
if (!needAddTile && !Arrays.equals(origin, merged)) {
// if origin and merged line is different
// need to add a new Tile in the board
needAddTile = true;
}
}
// if needAddTile is false, those line didn't change
// then no need to add tile
if (needAddTile) {
addTile();
}
}
/**
* move tiles to the right side.
*/
public void right() {
tiles = rotate(180);
left();
tiles = rotate(180);
}
/**
* move tiles up.
*/
public void up() {
tiles = rotate(270);
left();
tiles = rotate(90);
}
/**
* move tiles down.
*/
public void down() {
tiles = rotate(90);
left();
tiles = rotate(270);
}
/**
* get the Tile which at tiles[x + y * ROW ]
*/
private Tile tileAt(int x, int y) {
return tiles[x + y * ROW];
}
/**
* Generate a new Tile in the availableSpace.
*/
private void addTile() {
List<Integer> list = availableIndex();
int idx = list.get((int) (Math.random() * list.size()));
tiles[idx] = Tile.randomTile();
}
/**
* Query the tiles Array field, and get the list of empty tile's index. aka
* find the index is ok to add a new Tile.
*/
private List<Integer> availableIndex() {
List<Integer> list = new LinkedList<>();
for (int i = 0; i < tiles.length; i++) {
if (tiles[i].empty())
list.add(i);
}
return list;
}
/**
* return true if the board doesn't have empty tile
*/
private boolean isFull() {
return availableIndex().size() == 0;
}
/**
* return true if didn't lose
*/
boolean canMove() {
if (!isFull()) {
return true;
}
for (int x : _0123) {
for (int y : _0123) {
Tile t = tileAt(x, y);
if ((x < ROW - 1 && t.equals(tileAt(x + 1, y)))
|| (y < ROW - 1 && t.equals(tileAt(x, y + 1)))) {
return true;
}
}
}
return false;
}
/**
* rotate the tiles dgr degree, clockwise.
*
* @return Tile[], the tiles after rotate.
*/
private Tile[] rotate(int dgr) {
Tile[] newTiles = new Tile[ROW * ROW];
int offsetX = 3, offsetY = 3;
if (dgr == 90) {
offsetY = 0;
} else if (dgr == 180) {
} else if (dgr == 270) {
offsetX = 0;
} else {
throw new IllegalArgumentException(
"Only can rotate 90, 180, 270 degree");
}
double radians = Math.toRadians(dgr);
int cos = (int) Math.cos(radians);
int sin = (int) Math.sin(radians);
for (int x : _0123) {
for (int y : _0123) {
int newX = (x * cos) - (y * sin) + offsetX;
int newY = (x * sin) + (y * cos) + offsetY;
newTiles[(newX) + (newY) * ROW] = tileAt(x, y);
}
}
return newTiles;
}
/**
* move the not empty tile line to left
*/
private Tile[] moveLine(Tile[] oldLine) {
LinkedList<Tile> l = new LinkedList<>();
for (int i : _0123) {
if (!oldLine[i].empty())
l.addLast(oldLine[i]);
}
if (l.size() == 0) {
// list empty, oldLine is empty line.
return oldLine;
} else {
Tile[] newLine = new Tile[4];
ensureSize(l, 4);
for (int i : _0123) {
newLine[i] = l.removeFirst();
}
return newLine;
}
}
/**
* Merge the oldLine of Tiles, then return a newLine
*/
private Tile[] mergeLine(Tile[] oldLine) {
LinkedList<Tile> list = new LinkedList<Tile>();
for (int i = 0; i < ROW; i++) {
if (i < ROW - 1 && !oldLine[i].empty()
&& oldLine[i].equals(oldLine[i + 1])) {
// can be merge, double the val
Tile merged = oldLine[i].getDouble();
i++; // skip next one!
list.add(merged);
if (merged.value() == GOAL) {
// reach goal, show message
host.win();
}
} else {
list.add(oldLine[i]);
}
}
ensureSize(list, 4);
return list.toArray(new Tile[4]);
}
/**
* Append the empty tile to the l list of tiles, ensure it's size is s.
*/
private static void ensureSize(List<Tile> l, int s) {
while (l.size() < s) {
l.add(Tile.ZERO);
}
}
/**
* get the idx-th line.
*/
private Tile[] getLine(int idx) {
Tile[] result = new Tile[4];
for (int i : _0123) {
result[i] = tileAt(i, idx);
}
return result;
}
/**
* set the idx-th line. replace by the re array.
*/
private void setLine(int idx, Tile[] re) {
for (int i : _0123) {
tiles[i + idx * ROW] = re[i];
}
}
/* Background color */
private static final Color BG_COLOR = new Color(0xcbada0);
/* Font */
private static final Font STR_FONT = new Font(Font.SANS_SERIF,
Font.BOLD, 17);
@Override
public void paint(Graphics g) {
super.paint(g);
g.setColor(BG_COLOR);
g.setFont(STR_FONT);
g.fillRect(0, 0, this.getSize().width, this.getSize().height);
for (int y : _0123) {
for (int x : _0123) {
drawTile(g, tiles[x + y * ROW], x, y);
}
}
}
/* Side of the tile square */
private static final int SIDE = 64;
/* Margin between tiles */
private static final int MARGIN = 16;
/**
* Draw a tile use specific number and color in (x, y) coords, x and y need
* offset a bit.
*/
private void drawTile(Graphics g, Tile tile, int x, int y) {
Value val = tile.value();
int xOffset = offsetCoors(x);
int yOffset = offsetCoors(y);
g.setColor(val.color());
g.fillRect(xOffset, yOffset, SIDE, SIDE);
g.setColor(val.fontColor());
if (val.score() != 0)
g.drawString(tile.toString(), xOffset
+ (SIDE >> 1) - MARGIN, yOffset + (SIDE >> 1));
}
private static int offsetCoors(int arg) {
return arg * (MARGIN + SIDE) + MARGIN;
}
}
package game;
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
/**
*
* @author sibidharan
*/
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class GUI2048 extends JFrame {
private static final long serialVersionUID = 1L;
JLabel statusBar;
private static final String TITLE = "2048 Java Swing";
public static final String WIN_MSG = "Wow! You won. But, push your limits.";
public static final String LOSE_MSG = "Oops. You lost. Hit 'R' to try again.";
public static void main(String[] args) {
GUI2048 game = new GUI2048();
Board board = new Board(game);
if (args.length != 0 && args[0].matches("[0-9]*")) {
Board.GOAL = Value.of(Integer.parseInt(args[0]));
}
KeySetting kb = KeySetting.getkeySetting(board);
board.addKeyListener(kb);
game.add(board);
game.setLocationRelativeTo(null);
game.setVisible(true);
}
public GUI2048() {
setTitle(TITLE);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(340, 400);
setResizable(false);
statusBar = new JLabel("");
add(statusBar, BorderLayout.SOUTH);
}
void win() {
statusBar.setText(WIN_MSG);
}
void lose() {
statusBar.setText(LOSE_MSG);
}
}
package game;
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
/**
*
* @author sibidharan
*/
import static java.awt.event.KeyEvent.VK_DOWN;
import static java.awt.event.KeyEvent.VK_H;
import static java.awt.event.KeyEvent.VK_J;
import static java.awt.event.KeyEvent.VK_K;
import static java.awt.event.KeyEvent.VK_L;
import static java.awt.event.KeyEvent.VK_LEFT;
import static java.awt.event.KeyEvent.VK_R;
import static java.awt.event.KeyEvent.VK_RIGHT;
import static java.awt.event.KeyEvent.VK_UP;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
public class KeySetting extends KeyAdapter {
private static final HashMap<Integer, Method> keyMapping = new HashMap<>();
private static Integer[] KEYS = { VK_UP, VK_DOWN, VK_LEFT, VK_RIGHT, VK_R };
private static Integer[] VI_KEY = { VK_K, VK_J, VK_H, VK_L };
private static String[] methodName = { "up", "down", "left", "right", "initTiles" };
private static Board board;
private static final KeySetting INSTANCE = new KeySetting();
public static KeySetting getkeySetting(Board b) {
board = b;
return INSTANCE;
}
// Singleton
private KeySetting() {
initKey(KEYS);
initKey(VI_KEY);
}
/**
* initialize keycode in the kcs array.
*/
void initKey(Integer[] kcs) {
for (int i = 0; i < kcs.length; i++) {
try {
keyMapping.put(kcs[i], Board.class.getMethod(methodName[i]));
} catch (NoSuchMethodException | SecurityException e) {
e.printStackTrace();
}
}
}
/**
* Use reflect to invoke the mapping method.
*/
@Override
public void keyPressed(KeyEvent k) {
super.keyPressed(k);
Method action = keyMapping.get(k.getKeyCode());
if (action == null) {
System.gc();
return;
}
try {
action.invoke(board);
board.repaint();
} catch (InvocationTargetException | IllegalAccessException
| IllegalArgumentException e) {
e.printStackTrace();
}
if (!board.canMove()) { // can not move, game over
board.host.lose();
}
}
}
package game;
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
/**
*
* @author sibidharan
*/
import static game.Value._0;
import static game.Value._2;
import static game.Value._4;
import java.util.HashMap;
public class Tile {
private final Value val;
private final static HashMap<Integer, Tile> cache = new HashMap<>();
/**
* Frequently used tile, reuse these whenever possible
*/
public final static Tile ZERO = new Tile(_0);
public final static Tile TWO = new Tile(_2);
public final static Tile FOUR = new Tile(_4);
static {
for (Value v : Value.values()) {
switch (v) {
case _0:
cache.put(v.score(), ZERO);
break;
case _2:
cache.put(v.score(), TWO);
break;
case _4:
cache.put(v.score(), FOUR);
break;
default:
cache.put(v.score(), new Tile(v));
break;
}
}
}
public Tile(Value v) {
val = v;
}
/*
* factory method to get Tile instance
*/
public static Tile valueOf(int num) {
return cache.get(num);
}
Value value() {
return val;
}
/**
* Use for merge, double the score
*
* @return a new Tile which's val multiply 2