In: Computer Science
In Java please:
1) Part 1: Edit the getTileList method (add length method too)
Dear Developer, It seems an overzealous programmer tried to create
a Fibonacci slider puzzle from our old code. This brought up the
fact there is a data integrity issue in our SlidingSquarePuzzle
class. It makes sense because the class’s data only consists of an
int[]. Since, you are new this is a good opportunity to get your
feet wet. I want you to change the offending code and help shore up
our security hole. I’ve attached their Driver and the GUI and
SlidingSquarePuzzle files. I’ve also commented out the offending
code. Sincerely, Your Boss P.S. You might as well include some
documentation while you’re at it. It’s been bugging me for the
longest time.
Part 1: Edit the getTileList method
1. Why does this method create a security issue? a. Because you have direct access to the array. As seen in the driver, you have the reference to the array.
2. Change the method to getTile(int index) and return an int at the specific index of tileList.
3. This will break the GUI code. You will need to replace instances of getTileList with getTile. a. Even though you have changed everything, your code will not work. b. This is due to the return data being primitive. You cannot assign data to a primitive data type.
4. Add a length method that returns the tileList.length. a. Replace where necessary.
=================
Driver.java:
public class Driver { public static void main(String[] args) { //change this number to how many tiles you want int numberOfTiles = 9; SlidingSquarePuzzle fibonaciSlider = new SlidingSquarePuzzle(numberOfTiles); // fibonaciSlider.getTileList()[0] = 1; // fibonaciSlider.getTileList()[1] = 1; // fibonaciSlider.getTileList()[2] = 2; // fibonaciSlider.getTileList()[3] = 3; // fibonaciSlider.getTileList()[4] = 5; // fibonaciSlider.getTileList()[5] = 8; // fibonaciSlider.getTileList()[6] = 13; // fibonaciSlider.getTileList()[7] = 21; GUI.init(fibonaciSlider); } }
=====================
GUI.java
import java.awt.*; import java.awt.event.*; import javax.swing.*; @SuppressWarnings("serial") public class GUI extends JPanel { private SlidingSquarePuzzle ssp; private final int sideLength; private final int tileSize; private int blankPos; private final int margin; private final int gridSize; private boolean gameOver; private GUI(SlidingSquarePuzzle ssp) { this.ssp = ssp; sideLength = (int) Math.floor(Math.sqrt(ssp.getTileList().length)); final int resolution = 640; margin = 40; tileSize = (resolution - 2 * margin) / sideLength; gridSize = tileSize * sideLength; setPreferredSize(new Dimension(resolution, resolution + margin)); setBackground(Color.WHITE); setForeground(new Color(0x006940)); setFont(new Font("SansSerif", Font.BOLD, 60)); gameOver = true; addMouseListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { if (gameOver) { newGame(); } else { int ex = e.getX() - margin; int ey = e.getY() - margin; if (ex < 0 || ex > gridSize || ey < 0 || ey > gridSize) return; int clickX = ex / tileSize; int clickY = ey / tileSize; int blankX = blankPos % sideLength; int blankY = blankPos / sideLength; int clickPos = clickY * sideLength + clickX; int direction = 0; if (clickX == blankX && Math.abs(clickY - blankY) > 0) { direction = (clickY - blankY) > 0 ? sideLength : -sideLength; } else if (clickY == blankY && Math.abs(clickX - blankX) > 0) { direction = (clickX - blankX) > 0 ? 1 : -1; } if (direction != 0) { do { int newBlankPos = blankPos + direction; ssp.getTileList()[blankPos] = ssp.getTileList()[newBlankPos]; blankPos = newBlankPos; } while (blankPos != clickPos); ssp.getTileList()[blankPos] = 0; } gameOver = ssp.isSolved(); } repaint(); } }); newGame(); } private void newGame() { ssp.shuffle(); gameOver = false; for (int i = 0; i < ssp.getTileList().length; i++) { if(ssp.getTileList()[i] != 0) continue; blankPos = i; break; } } private void drawGrid(Graphics2D g) { for (int i = 0; i < ssp.getTileList().length; i++) { int x = margin + (i % sideLength) * tileSize; int y = margin + (i / sideLength) * tileSize; if (ssp.getTileList()[i] == 0) { if (gameOver) { g.setColor(Color.GREEN); drawCenteredString(g, "\u2713", x, y); } continue; } g.setColor(getForeground()); g.fillRoundRect(x, y, tileSize, tileSize, 25, 25); g.setColor(Color.green.darker()); g.drawRoundRect(x, y, tileSize, tileSize, 25, 25); g.setColor(Color.WHITE); drawCenteredString(g, String.valueOf(ssp.getTileList()[i]), x, y); } } private void drawStartMessage(Graphics2D g) { if (gameOver) { g.setFont(getFont().deriveFont(Font.BOLD, 18)); g.setColor(getForeground()); String s = "click to start a new game"; int x = (getWidth() - g.getFontMetrics().stringWidth(s)) / 2; int y = getHeight() - margin; g.drawString(s, x, y); } } private void drawCenteredString(Graphics2D g, String s, int x, int y) { FontMetrics fm = g.getFontMetrics(); int asc = fm.getAscent(); int des = fm.getDescent(); x = x + (tileSize - fm.stringWidth(s)) / 2; y = y + (asc + (tileSize - (asc + des)) / 2); g.drawString(s, x, y); } @Override public void paintComponent(Graphics gg) { super.paintComponent(gg); Graphics2D g = (Graphics2D) gg; g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); drawGrid(g); drawStartMessage(g); } public static void init(SlidingSquarePuzzle ssp) { SwingUtilities.invokeLater(() -> { JFrame f = new JFrame(); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setTitle("Slidng Square Puzzle"); f.setResizable(false); f.add(new GUI(ssp), BorderLayout.CENTER); f.pack(); f.setLocationRelativeTo(null); f.setVisible(true); }); } }
======================
SlidingSquarePuzzle.java
import java.util.Arrays; import java.util.Random; public class SlidingSquarePuzzle { private int [] tileList; public SlidingSquarePuzzle() { this(16); } public SlidingSquarePuzzle(int size) { tileList = size < 4 ? new int[4] : new int[(int) Math.pow(Math.floor(Math.sqrt(size)), 2)]; solve(); } public int[] getTileList() { return tileList; } public void solve() { for(int i = 0; i < tileList.length - 1; i++) { tileList[i] = i + 1; } } public boolean isSolved() { for(int i = 0; i < tileList.length - 1; i++) if (tileList[i] != i + 1) return false; return true; } /* * This looks at the puzzle and checks if it is solvable */ private boolean isSolvable() { int inversions = 0; int m = (int) Math.floor(Math.sqrt(tileList.length)); for(int i = 0; i < tileList.length; i++) for(int j = i + 1; j < tileList.length; j++) { if(tileList[i] == 0 || tileList[j] == 0) continue; if (tileList[j] < tileList[i]) inversions++; } if(m % 2 == 1) return inversions % 2 == 0; else { int blankIndex = 0; for(int i = 0; i < tileList.length; i++) if(tileList[i] == 0 ) { blankIndex = i; break; } if((blankIndex / m) % 2 == 0) return inversions % 2 == 1; else return inversions % 2 == 0; } } public void shuffle() { Random rand = new Random(); do { for(int i = tileList.length - 1; i > 0; i--) { int j = rand.nextInt(i + 1); int temp = tileList[j]; tileList[j] = tileList[i]; tileList[i] = temp; } }while(!this.isSolvable()); } }
---------------------------------------------------------------------------------------------------------------------------
Below are the three method required in SlidingSquarePuzzle.java file. Apart from given documentation , I also added settile() method to set the value in array.
-----------------------------------------------------------------------------------------------------------------------------
public int getTile(int index)
{
return
tileList[index];
}
public void setTile(int index,int
value)
{
tileList[index]=value;
}
public int length()
{
return
tileList.length;
}
========================================================================
Replace some code in GUI.java file
1. ssp.setTile(blankPos, ssp.getTile(newBlankPos)) in place of
ssp.getTileList()[blankPos] = ssp.getTileList()[newBlankPos];
2. replace ssp.getTileList().length to ssp.length()
3. replace ssp.getTileList()[index] to ssp.getTile(index)
========================================================================
-------------------------------------------------------------------------------------------------------------------------------
Below is required editable code.
------------------------------------------------SlidingSquarePuzzle.java----------------------------------------------
import java.util.Arrays;
import java.util.Random;
public class SlidingSquarePuzzle {
private int [] tileList;
public SlidingSquarePuzzle() {
this(16);
}
public SlidingSquarePuzzle(int size) {
tileList = size < 4
? new int[4]
: new int[(int) Math.pow(Math.floor(Math.sqrt(size)), 2)];
solve();
}
public int getTile(int index)
{
return
tileList[index];
}
public void setTile(int index,int
value)
{
tileList[index]=value;
}
public int length()
{
return
tileList.length;
}
public void solve() {
for(int i = 0; i < tileList.length - 1; i++) {
tileList[i] = i + 1;
}
}
public boolean isSolved() {
for(int i = 0; i < tileList.length - 1; i++)
if (tileList[i] != i + 1) return false;
return true;
}
/*
* This looks at the puzzle and checks if it is solvable
*/
private boolean isSolvable() {
int inversions = 0;
int m = (int) Math.floor(Math.sqrt(tileList.length));
for(int i = 0; i < tileList.length; i++)
for(int j = i + 1; j < tileList.length; j++)
{
if(tileList[i] == 0 || tileList[j] == 0) continue;
if (tileList[j] < tileList[i])
inversions++;
}
if(m % 2 == 1)
return inversions % 2 == 0;
else {
int blankIndex = 0;
for(int i = 0; i < tileList.length; i++)
if(tileList[i] == 0 ) {
blankIndex = i;
break;
}
if((blankIndex / m) % 2 == 0)
return inversions % 2 == 1;
else
return inversions % 2 == 0;
}
}
public void shuffle() {
Random rand = new Random();
do {
for(int i = tileList.length - 1; i > 0; i--) {
int j = rand.nextInt(i + 1);
int temp = tileList[j];
tileList[j] = tileList[i];
tileList[i] = temp;
}
}while(!this.isSolvable());
}
}
--------------------------------------------------GUI.java---------------------------------------------------------
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
@SuppressWarnings("serial")
public class GUI extends JPanel {
private SlidingSquarePuzzle ssp;
private final int sideLength;
private final int tileSize;
private int blankPos;
private final int margin;
private final int gridSize;
private boolean gameOver;
private GUI(SlidingSquarePuzzle ssp) {
this.ssp = ssp;
sideLength = (int) Math.floor(Math.sqrt(ssp.length()));
final int resolution = 640;
margin = 40;
tileSize = (resolution - 2 * margin) / sideLength;
gridSize = tileSize * sideLength;
setPreferredSize(new Dimension(resolution, resolution +
margin));
setBackground(Color.WHITE);
setForeground(new Color(0x006940));
setFont(new Font("SansSerif", Font.BOLD, 60));
gameOver = true;
addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
if (gameOver) {
newGame();
} else {
int ex = e.getX() - margin;
int ey = e.getY() - margin;
if (ex < 0 || ex > gridSize || ey < 0 || ey > gridSize)
return;
int clickX = ex / tileSize;
int clickY = ey / tileSize;
int blankX = blankPos % sideLength;
int blankY = blankPos / sideLength;
int clickPos = clickY * sideLength + clickX;
int direction = 0;
if (clickX == blankX && Math.abs(clickY - blankY) > 0)
{
direction = (clickY - blankY) > 0 ? sideLength :
-sideLength;
} else if (clickY == blankY && Math.abs(clickX - blankX)
> 0) {
direction = (clickX - blankX) > 0 ? 1 : -1;
}
if (direction != 0) {
do {
int newBlankPos = blankPos + direction;
ssp.setTile(blankPos, ssp.getTile(newBlankPos));
blankPos = newBlankPos;
} while (blankPos != clickPos);
ssp.setTile(blankPos,0);
}
gameOver = ssp.isSolved();
}
repaint();
}
});
newGame();
}
private void newGame() {
ssp.shuffle();
gameOver = false;
for (int i = 0; i < ssp.length(); i++) {
if(ssp.getTile(i) != 0) continue;
blankPos = i;
break;
}
}
private void drawGrid(Graphics2D g) {
for (int i = 0; i < ssp.length(); i++) {
int x = margin + (i % sideLength) * tileSize;
int y = margin + (i / sideLength) * tileSize;
if (ssp.getTile(i) == 0) {
if (gameOver) {
g.setColor(Color.GREEN);
drawCenteredString(g, "\u2713", x, y);
}
continue;
}
g.setColor(getForeground());
g.fillRoundRect(x, y, tileSize, tileSize, 25, 25);
g.setColor(Color.green.darker());
g.drawRoundRect(x, y, tileSize, tileSize, 25, 25);
g.setColor(Color.WHITE);
drawCenteredString(g, String.valueOf(ssp.getTile(i)), x, y);
}
}
private void drawStartMessage(Graphics2D g) {
if (gameOver) {
g.setFont(getFont().deriveFont(Font.BOLD, 18));
g.setColor(getForeground());
String s = "click to start a new game";
int x = (getWidth() - g.getFontMetrics().stringWidth(s)) / 2;
int y = getHeight() - margin;
g.drawString(s, x, y);
}
}
private void drawCenteredString(Graphics2D g, String s, int x, int
y) {
FontMetrics fm = g.getFontMetrics();
int asc = fm.getAscent();
int des = fm.getDescent();
x = x + (tileSize - fm.stringWidth(s)) / 2;
y = y + (asc + (tileSize - (asc + des)) / 2);
g.drawString(s, x, y);
}
@Override
public void paintComponent(Graphics gg) {
super.paintComponent(gg);
Graphics2D g = (Graphics2D) gg;
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
drawGrid(g);
drawStartMessage(g);
}
public static void init(SlidingSquarePuzzle ssp) {
SwingUtilities.invokeLater(() -> {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setTitle("Slidng Square Puzzle");
f.setResizable(false);
f.add(new GUI(ssp), BorderLayout.CENTER);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
});
}
}
-------------------------------------------------Driver.java-------------------------------------------------
public class Driver {
public static void main(String[] args) {
//change this number to how many tiles you want
int numberOfTiles = 9;
SlidingSquarePuzzle fibonaciSlider = new
SlidingSquarePuzzle(numberOfTiles);
// fibonaciSlider.getTileList()[0] = 1;
// fibonaciSlider.getTileList()[1] = 1;
// fibonaciSlider.getTileList()[2] = 2;
// fibonaciSlider.getTileList()[3] = 3;
// fibonaciSlider.getTileList()[4] = 5;
// fibonaciSlider.getTileList()[5] = 8;
// fibonaciSlider.getTileList()[6] = 13;
// fibonaciSlider.getTileList()[7] = 21;
GUI.init(fibonaciSlider);
}
}