Related
I am trying to make a scoreboard with images in processing, with images from 0 to 9, but any number greater than 9 does not make changes
It should be something like this: 10, 11, 12, ..., 99
but it only changes the number on the left, try using a counter, converting it to a String and then to a Char[] to get the first digit of two numbers; for example: 25, it would have to be 2
when passing that number to the array, it sends "ArrayIndexOutOfBoundsException"
char[] digits;
PImage [] prueba = new PImage[10];
int contadorPrueba2 = 0;
int aa = 0;
void setup () {
size (781, 470);
tablero = loadImage("tablero.png");
flechaRight = loadImage("flechaRight.png");
flechaLeft = loadImage("flechaLeft.png");
for (int i = 0; i < prueba.length; i++) {
prueba[i] = loadImage("numero_" + i + ".jpg");
}
}
void draw () {
//flechas
image(flechaRight, x, y);
image(flechaLeft, x2, y);
image(prueba[0], x3, 50);
//cambiar de numeros
image(prueba[contadorPrueba2], x4, 50);
image(prueba[aa], x3, 50);
}
boolean isMouseOver (int x, int y, int w, int h) {
if (mouseX>x && mouseX <x+w && mouseY>y && mouseY <y+h) {
return true;
}
return false;
}
void mousePressed () {
if (isMouseOver (x, y, w, h) == true) {
contadorPrueba2++;
//image(uno, x3, 50);
} else if (isMouseOver (x2, y, w, h) == true) {
contadorPrueba2--;
}
if (contadorPrueba2 >= prueba.length)
contadorPrueba2 = 0;
count = String.valueOf(contadorPrueba2);
digits = count.toCharArray();
for (int i = 0; i < digits.length; i++) {
if (contadorPrueba2 >= 10) {
//aa = digits[0];
println(digits[i]);
aa = digits[i];
//aa = digits[i];
//print("pp" + aa);
if (i == 0) {
print("ksk" + digits[i]);
}
}
}
}
Chars aren't exactly the best way to keep track of a score, which can make for some headache at times. I strongly suggest that you keep track of the score with an integer number unless you really have no choice on the matter.
Now, to translate an integer into a bunch of index numbers associated with images of said numbers, things can also become complicated, but I got your back! In fact, you can use MATH and solve this quite easily. Are you familiar with the modulo operator? If you're not, read about it because it's a programmer's best friend. Long story short, it's a division that returns only the leftover numbers after the division. As an example, if I write:
10 / 3 == 3.333333 // as a division this makes sense
10 % 3 == 1 // modulo only keeps what's left when the division stops being an integer
because: 10 == [3+3+3] + 1
Ok, you probably already knew this, but if you didn't, now you do. Here's how I use this knowledge to simplify your issue with a commented example:
PImage[] digits = new PImage[10];
int score = 4780; // you can change this number for whatever integer number
void setup () {
size(200, 200);
for (int i = 0; i < digits.length; i++) {
digits[i] = loadImage(i + ".png"); // these 10 images are 10x10 pixels for easier handling
}
}
void draw () {
int i=1;
int j = 160; // arbitrary number: this is where i start drawing the score (the rightest number)
// oooh! This is a good opportunity to use a do...while(); loop! I don't have that many of those.
// This is because we have to show at least ONE digit even if the score is zero, but I coded this so you can have a score higher than 99 without issue
do {
i*=10; // using multiples of 10 with the modulo operator
// as we use base 10 in real life, multiples of 10 help isolate digits of interests
image(digits[(score%i)/(i/10)], j, 90); // j is the x coordinate of the current digit, 90 is an arbitrary y coordinate
// 'digits[(score%i)/(i/10)]' deserves an explanation:
// 'score%i' removes every unit besides the current digit of interests, as an example if we're looking for the hundreds digit of 3456 it'll be 400
// '/(i/10)' removes the unwanted zero (in the 3456 example it would leave only the number 4 instead of 400)
j-=10; // updating j for the next digit
} while(i<score);
}
I know I didn't tell you why you get ArrayIndexOutOfBoundsException and it's kinda on purpose: this is a very common error and although I have no trouble guessing why you get it, it's just more efficient to fix by improving the method than by meddling with the code. There are many articles on SO about why this error happens and I encourage you to read at least one, as it'll be something that you'll see again in the future. Yet, for now, you can just avoid it by switching to this method.
I hope this helps. Have fun!
I have a task revolving around a small game, called Lights Out.
Game
The game consists of a board with dimensions 3x3, where each cell can either be 1 or 0, for example:
0 1 0
1 1 0
0 0 0
the game is said to be solved when all cells are 1, so:
1 1 1
1 1 1
1 1 1
and in each turn the user can click any cell which will flip its state and the state of the neighbors to the left, right, above and below (if they exist). So clicking on the cell in the middle of the first example board will yield:
0 0 0
0 0 1
0 1 0
Task
Now I have to find the worst possible initial board for the game and also figure out how many turns it needs to the solved state if played optimal.
Attempt
I tried to write a recursive solver which, given an initial board, finds the optimal sequence of turns to solve the game. And after that I wanted to feed it with all possible initial boards.
However, the recursion runs into a stack overflow. So I probably have to rewrite it in an iterative fashion. How can I do that?
Here is the code, as minimal complete example:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.StringJoiner;
import java.util.stream.Collectors;
public class GameTest {
public static void main(String[] args) {
boolean[][] board = {
{false, false, false},
{false, true, false},
{false, false, false}
};
List<GameState> solutionPath = GameSolver.solve(board);
printSolutionPath(solutionPath);
}
private static void printSolutionPath(List<GameState> solutionPath) {
System.out.printf("Solution path uses %d turns%n", solutionPath.get(solutionPath.size() - 1).getTurns());
String turnProgression = solutionPath.stream()
.map(state -> String.format("[%d|%d]", state.getX(), state.getY()))
.collect(Collectors.joining(" -> "));
System.out.println("Turns are: " + turnProgression);
System.out.println("Board progression is:");
for (GameState state : solutionPath) {
System.out.println(state.boardToString());
System.out.println("-----");
}
}
private static class GameSolver {
public static List<GameState> solve(boolean[][] initialBoard) {
GameState state = new GameState(initialBoard);
return solve(state);
}
public static List<GameState> solve(GameState state) {
// Base case
if (state.isSolved()) {
return List.of(state);
}
// Explore all other solutions
List<List<GameState>> solutionPaths = new ArrayList<>();
boolean[][] board = state.getBoard();
for (int x = 0; x < board.length; x++) {
for (int y = 0; y < board[x].length; y++) {
solutionPaths.add(solve(new GameState(state, x, y)));
}
}
List<GameState> bestSolutionPath = Collections.min(solutionPaths, Comparator.comparingInt(solutionPath -> solutionPath.get(solutionPath.size() - 1).getTurns()));
bestSolutionPath.add(state);
return bestSolutionPath;
}
}
private static class GameState {
private boolean[][] board;
private int turns;
private int x;
private int y;
public GameState(boolean[][] board) {
this.board = board;
turns = 0;
x = -1;
y = -1;
}
public GameState(GameState before, int x, int y) {
board = before.board;
click(x, y);
turns++;
this.x = x;
this.y = y;
}
public boolean isSolved() {
for (boolean[] row : board) {
for (boolean state : row) {
if (!state) {
return false;
}
}
}
return true;
}
public int getTurns() {
return turns;
}
public boolean[][] getBoard() {
return board;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public String boardToString() {
StringBuilder sb = new StringBuilder();
for (int x = 0; x < board.length; x++) {
StringJoiner row = new StringJoiner(" ");
for (int y = 0; y < board[x].length; y++) {
row.add(board[x][y] ? "1" : "0");
}
sb.append(row);
}
return sb.toString();
}
private void click(int centerX, int centerY) {
toggle(centerX, centerY);
toggle(centerX, centerY - 1);
toggle(centerX, centerY + 1);
toggle(centerX - 1, centerY);
toggle(centerX + 1, centerY);
}
private void toggle(int x, int y) {
if (x < 0 || y < 0 || x >= board.length || y >= board[x].length) {
return;
}
board[x][y] = !board[x][y];
}
}
}
Algorithm
If possible, I would also be interested in pure-mathematical arguments that solve or prove this without writing code that solves it by trying out.
The "Lights Out" problem can be simplified by observing that the moves are commutative, i.e. if you flip the plus-shapes centred on a certain set of cells, then it doesn't matter which order you flip them in. So an actual ordered path through a graph is not needed. We can also observe that each move is self-inverse, so no solution requires making the same move more than once, and if a set of moves m is a solution to a position p, then m also produces the position p starting from an empty board.
Here's a short solution in Python based on this observation: I've solved it for the goal of all 0s, i.e. the "lights" are "out", but it is trivial to change it to solve for the goal of all 1s.
The constant list masks represents which cells should be flipped for each of the 9 possible moves.
The bitcount function is used to measure how many moves a solution takes, given a bitmask representing a subset of the 9 possible moves.
The position function computes the board position after a set of moves is made, using the exclusive-or operation to accumulate the results of multiple flips.
The positions dictionary maps each reachable board position to a list of move-sets which produce it starting from an empty board. It turns out that all positions are reachable by exactly one set of moves, but if this is not known in advance then a dictionary of lists gives a more general solution.
The max(..., min(...)) part finds the position maximising the minimum number of moves needed to solve it, as required.
masks = [
int('110100000', 2), int('111010000', 2), int('011001000', 2),
int('100110100', 2), int('010111010', 2), int('001011001', 2),
int('000100110', 2), int('000010111', 2), int('000001011', 2),
]
def bitcount(m):
c = 0
while m:
c += (m & 1)
m >>= 1
return c
def position(m):
r = 0
for i in range(9):
if (1 << i) & m:
r ^= masks[i]
return r
from collections import defaultdict
positions = defaultdict(list)
for m in range(2**9):
p = position(m)
positions[p].append(m)
solution = max(positions, key=lambda p: min(map(bitcount, positions[p])))
print('board:', bin(solution))
print('moves:', ', '.join(map(bin, positions[solution])))
Output:
board: 0b101010101
moves: 0b111111111
That is, the "worst initial position" is an X shape (all four corners plus the centre cell are 1s), and the solution is to perform all 9 moves.
I am proposing an iterative solution to solve this (and related problems) based on graph theory.
Shortest-Path-Problem (SSP)
The problem can be reformulated as shortest-path-problem and, by that, be solved with any standard SPP algorithm, for example Dijkstr's algorithm.
For that, we will interpret all possible game boards as vertices and the action of clicking cells as edges of a graph.
For example
0 1 0
1 1 0
0 0 0
will be a vertex in the graph with 9 outgoing edges in total (one for each cell to click at). So we will for example have an edge
0 1 0 0 0 0
1 1 0 --> 0 0 1
0 0 0 0 1 0
with cost 1. All edge costs will be 1, indicating counting turns.
Given an initial board, like above, we formulate the SPP as the task of finding the shortest path in this graph from the vertex representing the initial board to the vertex representing the solved state
1 1 1
1 1 1
1 1 1
By using standard algorithms for solving SSP we receive the optimal path and its total cost. The path is the sequence of game states and the total cost is the amount of turns needed for that.
*-1 SPP
However, you are not only interested in solving given initial boards but also in finding the worst initial board and its optimal amount of turns.
This can be reformulated as a variant of the SPP family, namely trying to find the longest shortest path to the solved state. This is, among all shortest paths in the graph that end in the solved state, the path that maximizes the total cost.
This can be computed efficiently by a *-1 (many-to-one) SPP. That is, computing all shortest paths from any vertex to a single destination, which will be the solved state. And from those picking the path which has the greatest total cost.
Dijkstra's algorithm can compute that easily by executing the algorithm fully on a reversed graph (all edges reverse their direction) with the solved state as source, until it settled the whole graph (removing its stopping criteria).
Note that in your particular case graph reversal is not needed, as the graph in your game is bidirectional (any turn can be undone by executing it again).
Solution
Applying the above theory yields a pseudo-code looking like
Graph graph = generateGraph(); // all possible game states and turns
int[][] solvedState = [[1, 1, 1], [1, 1, 1], [1, 1, 1]];
List<Path> allShortestPaths = Dijkstra.shortestPathFromSourceToAllNodes(solvedState);
Path longestShortestPath = Collections.max(allPaths);
Some time ago I created a Java library for solving shortest path problems, Maglev. Using that library, the full code is:
import de.zabuza.maglev.external.algorithms.Path;
import de.zabuza.maglev.external.algorithms.ShortestPathComputationBuilder;
import de.zabuza.maglev.external.graph.Graph;
import de.zabuza.maglev.external.graph.simple.SimpleEdge;
import de.zabuza.maglev.external.graph.simple.SimpleGraph;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Optional;
import java.util.StringJoiner;
public class GameTest {
public static void main(String[] args) {
Graph<GameState, SimpleEdge<GameState>> graph = generateGraph();
var algo = new ShortestPathComputationBuilder<>(graph).resetOrdinaryDijkstra()
.build();
GameState solvedState =
new GameState(new boolean[][] { { true, true, true }, { true, true, true }, { true, true, true } });
var pathTree = algo.shortestPathReachable(solvedState);
var longestShortestPath = pathTree.getLeaves()
.stream()
.map(pathTree::getPathTo)
.map(Optional::orElseThrow)
.max(Comparator.comparing(Path::getTotalCost))
.orElseThrow();
System.out.println("The longest shortest path has cost: " + longestShortestPath.getTotalCost());
System.out.println("The states are:");
System.out.println(longestShortestPath.iterator().next().getEdge().getSource());
for (var edgeCost : longestShortestPath) {
System.out.println("------------");
System.out.println(edgeCost.getEdge().getDestination());
}
}
private static Graph<GameState, SimpleEdge<GameState>> generateGraph() {
SimpleGraph<GameState, SimpleEdge<GameState>> graph = new SimpleGraph<>();
generateNodes(graph);
generateEdges(graph);
return graph;
}
private static void generateNodes(Graph<GameState, SimpleEdge<GameState>> graph) {
for (int i = 0; i < 1 << 9; i++) {
String boardString = String.format("%09d", Integer.parseInt(Integer.toBinaryString(i)));
graph.addNode(GameState.of(boardString, 3, 3));
}
}
private static void generateEdges(Graph<GameState, SimpleEdge<GameState>> graph) {
for (GameState source : graph.getNodes()) {
// Click on each field
boolean[][] board = source.getBoard();
for (int x = 0; x < board.length; x++) {
for (int y = 0; y < board[x].length; y++) {
GameState destination = new GameState(board);
destination.click(x, y);
graph.addEdge(new SimpleEdge<>(source, destination, 1));
}
}
}
}
private static class GameState {
public static GameState of(String boardString, int rows, int columns) {
boolean[][] board = new boolean[rows][columns];
int i = 0;
for (int x = 0; x < rows; x++) {
for (int y = 0; y < columns; y++) {
board[x][y] = boardString.charAt(i) == '1';
i++;
}
}
return new GameState(board);
}
private final boolean[][] board;
private GameState(boolean[][] board) {
this.board = new boolean[board.length][];
for (int x = 0; x < board.length; x++) {
this.board[x] = new boolean[board[x].length];
for (int y = 0; y < board[x].length; y++) {
this.board[x][y] = board[x][y];
}
}
}
public boolean[][] getBoard() {
return board;
}
#Override
public String toString() {
StringJoiner rowJoiner = new StringJoiner("\n");
for (int x = 0; x < board.length; x++) {
StringJoiner row = new StringJoiner(" ");
for (int y = 0; y < board[x].length; y++) {
row.add(board[x][y] ? "1" : "0");
}
rowJoiner.add(row.toString());
}
return rowJoiner.toString();
}
#Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final GameState gameState = (GameState) o;
return Arrays.deepEquals(board, gameState.board);
}
#Override
public int hashCode() {
return Arrays.deepHashCode(board);
}
private void click(int x, int y) {
toggle(x, y);
toggle(x, y - 1);
toggle(x, y + 1);
toggle(x - 1, y);
toggle(x + 1, y);
}
private void toggle(int x, int y) {
if (x < 0 || y < 0 || x >= board.length || y >= board[x].length) {
return;
}
board[x][y] = !board[x][y];
}
}
}
Which yields the following solution to your problem:
The longest shortest path has cost: 9.0
The states are:
1 1 1
1 1 1
1 1 1
------------
1 0 1
0 0 0
1 0 1
------------
1 0 1
1 0 0
0 1 1
------------
1 1 0
1 0 1
0 1 1
------------
1 1 0
1 0 0
0 0 0
------------
1 1 0
1 1 0
1 1 1
------------
0 0 1
1 0 0
1 1 1
------------
1 0 1
0 1 0
0 1 1
------------
0 1 1
1 1 0
0 1 1
------------
0 1 0
1 0 1
0 1 0
So the worst initial game state is
0 1 0
1 0 1
0 1 0
and, if played optimally, it needs 9 turns to solve the game.
Some trivia, the game has 512 states in total (2^9) and 4608 possible moves.
Treat the puzzle as a graph per Zabuzard's answer, then perform breadth-first search starting from the solved node. The last node you reach is among the set having the longest-shortest path to the solution.
If possible, I would also be interested in pure-mathematical arguments that solve or prove this without writing code that solves it by trying out.
I am proposing a solution purely based on linear algebra.
Board as matrix
The game can be interpreted as set of linear equations which can be solved using standard linear equation solving techniques.
For that, a game board is interpreted as matrix .
In total, there are 9 possible actions (one for clicking each cell of the board). We encode which cells have to be flipped per action in 9 corresponding matrices:
where is the action-matrix corresponding to a click on cell in row i and column j.
Actions are commutative and self-inverse
Since entries are in , applying an action to a given board is as simple as adding the corresponding action-matrix to the board-matrix. For example:
This means that applying a set of actions is nothing else than some matrix additions. Matrix additions are commutative. This means that the order in which they are applied does not matter:
Moreover, any action-matrix is self-inverse on addition. Applying it again undoes the action, i.e.
This follows that, for any initial game board, we only have to apply each action at most once and the order in which we do that does not matter.
Linear equation system
This leads to the equation:
With the initial game board matrix L, coefficients which are 1 if the action should be applied, otherwise 0; and 1 being the all-ones matrix indicating that the game is won.
The equation can be simplified by moving L to the other side:
where L* is L but with all cells flipped.
Finally, this equation can be rewritten as standard linear system of equations Ax = b which can then be solved easily:
Since this matrix has maximal rank and a non-zero determinant , the game on a 3x3 board is always solvable and a solution is given by simply solving the linear equation system or by applying Cramer's rule.
Worst initial board
It also follows that the worst initial board is a matrix L that maximizes the coefficients being used, ideally all 9.
Turns out
0 1 0
1 0 1
0 1 0
is such an initial board which needs all 9 coeeficients to be set for a solution. I.e. solving the system
yields exactly one solution, namely for all i, j.
This can also be obtained from the opposite direction by setting all coefficients to 1 and solving for L instead:
which yields
0 1 0
1 0 1
0 1 0
for L again.
I'm tinkering around with a cellular automaton and my movement detection function is acting really strangely. I'm 80% sure it's my implementation but I have no idea where the issue is. Could someone take a look and enlighten me since I've spent the better part of 7H trying to get it to work and it won't:
private int[] cellularSearch(short xPos, short yPos)
{
// the center position is our current position; the others are potentially free positions
byte[][] positions = new byte[][]{{0,0,0},{0,1,0},{0,0,0}};
int[] result = new int[2];
byte strike=0;
int dice0=0, dice1=0;
while(strike<9)
{
dice0 = r.nextInt(3)-1;
result[0] = xPos + dice0;
if((result[0] >= 0)
&& (result[0] < img.getWidth()))
{
dice1 = r.nextInt(3)-1;
result[1] = yPos + dice1;
if((result[1] >= 0)
&& (result[1] < img.getHeight()))
{
if((positions[dice1+1][dice0+1] != 1)) // if this isn't our own cell and wasn't tried before
{
if(img.getRGB(result[0], result[1]) == Color.white.getRGB()) // if the new cell is free
{
return result;
}
}
positions[dice1+1][dice0+1]=1; // we need to use +1 to create a correlation between the linkage in the matrix and the actual positions around our cell
strike++;
}
}
}
}
The code works and it correctly identifies when a pixel is white and returns the position for it. My problem is the distribution of the results. Given that I'm using Random both for the row and the column, I was expecting a near equal distribution over all possible locations, but what happens is that this code seems to prefer the cell right above the coordinates being fed in (it hits it ~3x as much as the other ones) and the one right below the coordinates (it hits it ~2x as much as the others).
When I start my program and all my pixels slowly move towards the top of the window on EVERY run (vs true randomness with my old lengthy code which was 3x as long), so there's gotta be an error in there somewhere. Could someone please lend a hand?
Thank you in advance!
EDIT: Thank you everyone for the effort! Sorry for the non-compiling code but I extracted the main purpose of the function while cutting out a ton of commented code (my other approaches to implementing this function). Locally the code has the return statement and it runs. I'll slowly go through all your answers in the next few hours (gonna have dinner soon).
EDIT2: I tried what #DodgyCodeException and #tevemadar suggested and made a list with all the 8 positions, then shuffle them each time I enter the function, and then iterate through them, trying each one in part. Still the position exactly above and below the current cell are selected most. I'm baffled. This is my old super-spaghetti code that I've written for this function and it worked perfectly with no errors, equal distribution, and (oddly enough) it's the most efficient implementation that I've tried out of everything mentioned here. After I'm done with lunch and filing some paperwork I'll thoroughly study it (it's been ~ 2 years since I wrote it) to see why it works so well. If anyone still has ideas, I'm fully open.
boolean allRan=false;
int lastDice=0, anteLastDice=0, dice = r.nextInt(3)+1;
//the initial dice usage is for selecting the row on which we'll operate:
//dice = 1 or 3 -> we operate above or under our current cell; dice = 2 -> we operate on the same row
while(!allRan)
{
if((dice==1) || (dice==3))
{
int i= r.nextInt(3);
if(((xPos-1+i) < img.getWidth())
&& ((xPos-1+i) >= 0))
{
if(((yPos-1) >= 0)
&& (img.getRGB(xPos-1+i, yPos-1) == Color.white.getRGB())
&& (dice==1))
{
result[0] = xPos-1+i;
result[1] = yPos-1;
above++;
endTime = (int) System.currentTimeMillis();
section4Runtime += (double) (endTime - startTime) / 1000;
return result;
}
else if(((yPos+1) < img.getHeight())
&& (img.getRGB(xPos-1+i, yPos+1) == Color.white.getRGB())
&& (dice==3))
{
result[0] = xPos-1+i;
result[1] = yPos+1;
below++;
endTime = (int) System.currentTimeMillis();
section4Runtime += (double) (endTime - startTime) / 1000;
return result;
}
}
// if this section is reached, it means that: the initial dice roll didn't find a free cell, or the position was out of bounds, or the dice rolled 2
// in this section we do a dice reroll (while remembering and avoiding our previous values) so that we cover all dice rolls
if(dice==1)
{
if(lastDice==0)
{
lastDice=dice;
dice += r.nextInt(2)+1; // we incrmeent randomly towards 2 or 3.
}
else
{
if(lastDice==2)
{
if(anteLastDice==0)
{
anteLastDice= lastDice;
lastDice=dice;
dice=3;
}
else
{
allRan=true;
}
}
else if(lastDice==3)
{
if(anteLastDice==0)
{
anteLastDice= lastDice;
lastDice=dice;
dice=2;
}
else
{
allRan=true;
}
}
}
}
else // dice is 3
{
if(lastDice==0)
{
lastDice=dice;
dice -= r.nextInt(2)+1; // we decrement randomly towards 2 or 1.
}
else
{
if(lastDice==2)
{
if(anteLastDice==0)
{
anteLastDice= lastDice;
lastDice=dice;
dice=1;
}
else
{
allRan=true;
}
}
else if(lastDice==1)
{
if(anteLastDice==0)
{
anteLastDice= lastDice;
lastDice=dice;
dice=2;
}
else
{
allRan=true;
}
}
}
}
}
if(dice==2)
{
int i=0;
i += r.nextInt(2)==0?-1:1;
if(((xPos+i) < img.getWidth())
&& ((xPos+i) >= 0)
&& (img.getRGB(xPos+i, yPos) == Color.white.getRGB()))
{
result[0] = xPos+i;
result[1] = yPos;
leveled++;
endTime = (int) System.currentTimeMillis();
section4Runtime += (double) (endTime - startTime) / 1000;
return result;
}
// same as above: a dice reroll (with constrictions)
if(lastDice==0)
{
lastDice=dice;
dice+= r.nextInt(2)==0?-1:1; // randomly chose if you decrement by 1 or increment by 1
}
else
{
if(lastDice==1)
{
if(anteLastDice==0)
{
anteLastDice= lastDice;
lastDice=dice;
dice =3;
}
else
{
allRan=true;
}
}
else if(lastDice==3)
{
if(anteLastDice==0)
{
anteLastDice= lastDice;
lastDice=dice;
dice =1;
}
else
{
allRan=true;
}
}
}
}
}
return result;
After much thought, I eventually figured it out. All the ideas that we all had were violating a fundamental "rule" of the first implementation that I was using: the first implementation was trying a random position on one of the 3 lines, then moving on to the next lines (without coming back to try the other positions on that line). Example: if the algo selected the line above, it would randomly try the top-left corner to see if it's free; if it wasn't then it would try the same line as the current cell and the line below (again, just with 1 of their possible positions) without coming back. All our ideas were iterating through all possibilities around the cell, which meant that it was inevitable to have the top and bottom line have more hits than the middle (since top and bottom have 3 possible points each while middle has only 2). Also, when there were holes in the field, the cells most likely to fill it up were the ones that were moving diagonally (which in the end is up or down) or those directly moving up or down, since those moving sideways only had the options left/ right. The only mystery that will remain unsolved is why (using our proposed implementations) the model would generally use the point exactly above our current cell. I have no idea why it loves going straight up most of the time with that implementation. Nevertheless, the new algorithm (which reflects the old one, but is much lighter) is:
boolean[] lines = new boolean[]{false, false, false};
byte checks =0;
while(checks < 3) // just 3 tries in total
{
dice = r.nextInt(3);
if(lines[dice]== false)
{
lines[dice] = true; // just 1 try per line
// calculated here since we reuse dice below
result[1] = yPos - 1 + dice; // will be above if dice==0; will be below if dice==2; same line if dice==1
if((dice == 0) || (dice == 2)) // top/bottom line
{dice = r.nextInt(3)-1;}
else if(dice == 1) // middle line
{dice = r.nextInt(2)==0?-1:1;} // we exclude the middle point since that's our current position
result[0] = xPos + dice; // logic is calculated above and just applied here
checks++;
}
if((result[0] >= 0)
&& (result[0] < img.getWidth())
&& (result[1] >= 0)
&& (result[1] < img.getHeight()))
{
if (img.getRGB(result[0], result[1]) == Color.white.getRGB()) // if the new cell is free
{
return result;
}
}
}
result[0] = -1; // in case we get here, reset the value so it's not used
This brings the code down from 167 lines to 33 lines (and makes it MUCH more readable). I have no idea who to select as the best solution. Please suggest if you have any ideas.
First, I have to admit I can't see what your algorithm is supposed to be doing -- it's not clear to me why you roll the each die when you do, other times using the existing value.
For a clear, easy to follow algorithm, I'd suggest scoping your dice variables inside the loop, rolling both at the same time, and making them final so that you know that each iteration has exactly one two-die roll:
while(strike < 9) {
final int roll1 = r.nextInt(3) - 1;
final int roll2 = r.nextInt(3) - 1;
strike += handleRoll(roll1,roll2);
}
You can prove the distribution to yourself by writing a simple counter for your handleRoll(), before later substituting your real code.
int[] counts = int[6];
void handleRoll(int roll1, int roll2) {
counts[1 + roll1] ++;
counts[4 + roll2] ++;
return 1;
}
(Increase the required strike count to get large enough samples to reason about)
Make sure you use the same instance of Random throughout the program -- don't keep making new ones.
(You could tidy this up a bit by creating a Coordinate class and a factory that creates random ones)
I simplified your code like this:
made a series of extract-method refactorings to tidy away detail
changed your rolls to use the range 0 to 2 instead of -1 to +1 -- since you use them in two places, and in one of those you add one again!
used x and y and only create result when needed
used final for the rolls and the resulting x and y, scoping them to the inside of the loop
turned nested ifs into an && logic
changed some odd type choices. The positions grid seems made for boolean. There's seldom any value in using short in Java.
So:
private int[] cellularSearch(int xPos, int yPos) {
boolean[][] positions =
new boolean[][] { { false, false, false },
{ false, true, false },
{ false, false, false } };
int strike = 0;
while (strike < 9) {
final int dice0 = r.nextInt(3);
final int dice1 = r.nextInt(3);
final int x = xPos + dice0 - 1;
final int y = yPos + dice1 - 1;
if (isInXrange(x) && isInYRange(y)) {
if (!alreadyTried(positions, dice1, dice0) && isWhite(x, y)) {
return new int[] { x, y };
}
markAsTried(positions, dice1, dice0);
strike++;
}
}
return null; // or whatever you intend to happen here
}
private boolean isInXrange(int x) {
return (x >= 0) && (x < img.getWidth());
}
private boolean isInYRange(int y) {
return (y >= 0) && (y < img.getHeight());
}
private boolean alreadyTried(boolean[][] positions, final int dice1, final int dice0) {
return positions[dice1 + 1][dice0 + 1];
}
private static void markAsTried(boolean[][] positions, int dice1, int dice0) {
positions[dice1][dice0] = true;
}
private boolean isWhite(final int x, final int y) {
return img.getRGB(x, y) == Color.white.getRGB();
}
I think this is equivalent to your code, with one exception -- yours doesn't roll the second die if the first roll takes you outside the width of the image. You could re-add this as a performance improvement later if you like.
But it exposes some issues. It looks as if the intent is to try every cell (you have a 3x3 grid, and you've chosen 9 "strikes") - but it doesn't increment strike when x,y is outside the image. It does increment strike when the position has been tried before. So you can exit the loop having not tried every cell.
I don't see a specific way that this causes the weighting you've described --
but it looks like the sort of thing that could lead to unexpected results.
(Anyway - since the code you've given doesn't compile, you didn't observe it with the code you've given us)
If the intention is to check every cell, it might be better to shuffle a list of cells to try, then test them in order:
List<Coords> coordsToTry = new ArrayList<>();
for(int x=0; x<2; x++) {
for(int y=0; y<2; y++) {
coordsToTry.add(new Coords( x, y));
}
}
Collections.shuffle(coordsToTry);
for(Coords coords : coordsToTry) {
if(isWhite(coords)) {
return coords;
}
}
return null; // or whatever is meant to happen when nothing found
The distribution of java.util.Random is not that uneven. You can confirm with the following code:
public static void main(String[] args) throws Exception {
final int N = 3;
Random r = new Random();
int[] counts = new int[N];
for (int i = 0; i <= 100_000; i++) {
counts[r.nextInt(N)]++;
}
System.out.println(Arrays.toString(counts));
}
UPDATE:
As you've said, the above code produces fairly evenly distributed values. However, add the following line at the beginning of the loop:
if (i % 6 == 0)
r = new Random(0);
And then you get [16667, 33334, 50000]. One value occurs twice as frequently, and another 3 times as frequently, as the first. This sets the random number generator to a newly created one with a constant seed. It simulates your code, in which you say you create a new Random() on entry to your function (albeit without a seed argument) and then your function calls nextInt() six times - this if (i % 6 == 0) statement ensures a new RNG is also created every 6 iterations.
Check your code and make sure you are only ever creating a RNG once in your whole program.
java.util.Random is a pseudorandom number generator (definition on wikipedia) and needs to be seeded.
From the docs:
If two instances of Random are created with the same seed, and the same sequence of method calls is made for each, they will generate and return identical sequences of numbers. In order to guarantee this property, particular algorithms are specified for the class Random.
If you want to be sure to get good random numbers, use SecureRandom, which is guaranteed to produce non-deterministic output
Since you are interested in the combined distribution of the two 'dices', on top of #DodgyCodeException's suggestion, you can check statistics like
public static void main(String[] args) {
Random r=new Random();
int stat[]=new int[9];
for(int i=0;i<9000;i++)
stat[r.nextInt(3)+r.nextInt(3)*3]++;
for (int i : stat)
System.out.println(i);
}
However it is pretty even too.
There is a minor difference between generating random numbers from a power-of-two-range and otherwise, so if you really want to do some magic, you can use the fact that you are actually picking a position out of 8 possibilities (since the middle one is ruled out at the beginning).
Something like
final int xhelper[]=new int[]{-1, 0, 1,-1, 1,-1, 0, 1};
final int yhelper[]=new int[]{-1,-1,-1, 0, 0, 1, 1, 1};
...
int dir=r.nextInt(8);
int dice0=xhelper[dir];
int dice1=yhelper[dir];
But in fact I do not think it makes a difference.
I was going through this problem in one of exam paper and found one solution in answer book. I am not able to understand algorithm behind it. Can anyone explain me how this algorithm works?
Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining.
For example, Given the input
[0,1,0,2,1,0,1,3,2,1,2,1]
the return value would be
6
Solution as per answer book is this
public class Solution {
public int trap(int[] height) {
if (height.length <=2 )
return 0;
int h = 0, sum = 0, i = 0, j = height.length - 1;
while(i < j)
{
if ( height[i] < height[j] )
{
h = Math.max(h,height[i]);
sum += h - height[i];
i++;
}
else
{
h = Math.max(h,height[j]);
sum += h - height[j];
j--;
}
}
return sum;
}
}
Thanks
WoDoSc was nice enough to draw a diagram of the elevations and trapped water. The water can only be trapped between two higher elevations.
What I did was run the code and output the results so you can see how the trapped water is calculated. The code starts at both ends of the "mountain" range. Whichever end is lower is moved closer to the center.
In the case where the two ends are the same height, the right end is moved closer to the center. You could move the left end closer to the center instead.
The first column is the height and index of the elevations on the left. The second column is the height and index of the elevations on the right.
The third column is the maximum minimum height. In other words, the maximum height of the left or the right, whichever maximum is smaller. This number is important to determine the local water level.
The fourth column is the sum.
Follow along with the diagram and you can see how the algorithm works.
0,0 1,11 0 0
1,1 1,11 1 0
1,1 2,10 1 0
0,2 2,10 1 1
2,3 2,10 2 1
2,3 1,9 2 2
2,3 2,8 2 2
2,3 3,7 2 2
1,4 3,7 2 3
0,5 3,7 2 5
1,6 3,7 2 6
6
And here's the code. Putting print and println statements in appropriate places can help you understand what the code is doing.
package com.ggl.testing;
public class RainWater implements Runnable {
public static void main(String[] args) {
new RainWater().run();
}
#Override
public void run() {
int[] height = { 0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1 };
System.out.println(trap(height));
}
public int trap(int[] height) {
if (height.length <= 2) {
return 0;
}
int h = 0, sum = 0, i = 0, j = height.length - 1;
while (i < j) {
System.out.print(height[i] + "," + i + " " + height[j] + "," + j
+ " ");
if (height[i] < height[j]) {
h = Math.max(h, height[i]);
sum += h - height[i];
i++;
} else {
h = Math.max(h, height[j]);
sum += h - height[j];
j--;
}
System.out.println(h + " " + sum);
}
return sum;
}
}
I know that probably it's not the best way to represent it graphically, but you can imagine the situation as the following figure:
Where the red bars are the terrain (with elevations according to the array of your example), and the blue bars are the water that can be "trapped" into the "valleys" of the terrain.
Simplifying, the algorithm loops all the bar left-to-right (if left is smaller) or right-to-left (if right is smaller), the variable h stores the maximum height found during each step of the loop, because the water can not be higher than the maximum height of the terrains, and to know how much water can be trapped, it sums the differences between the height of the water (maximum height h) and the elevation of the terrain on a specific point, to get the actual quantity of water.
The algorithm works by processing the land from the left (i) and the right (j).
i and j are counters that work towards each other approaching the middle of the land.
h is a variable that tracks the max height found thus far considering the lower side.
The land is processed by letting i and j worked "toward each other." When I read the code, I pictured two imaginary walls squeezing the water toward the middle where the lowest wall moves toward the higher wall. The algorithm continues to sum up the volume of water. It uses h - height[x] because water can only be contained by inside the lowest point between two walls. So essentially it continues to sum up the volume of water from the left and right and subtracts out and water displaced by higher elevation blocks.
Maybe better variable names would have been
leftWall instead of i
rightWall instead of j
waterMaxHeight instead
of h
I think above solution is difficult to understand.I have a simple solution which take o(n) extra space & o(n) time complexity.
Step of algorithm
1.Maintain an array which contain maximum of all element which is right side of current element.
2.maintain a variable max from left side which contain maximum of all element which is left side of current element.
3.find minimum of max from left & max from right which is already present in array.
4.if minimum value is greater than the current value in array than add difference of than in ans & add the difference with current value & update max from left.
import java.util.*;
import java.lang.*;
import java.io.*;
class Solution
{
public static void main (String[] args) throws java.lang.Exception
{
int[] array= {0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1 };
int[] arrayofmax=new int[array.length];
int max=0;
arrayofmax[array.length-1]=0;
for(int x=array.length-1;x>0;x--){
if(max<array[x]){
max=array[x];
}
arrayofmax[x-1]=max;
}
int ans=0;
int maxfromleft=0;
for(int i=0;i<array.length-1;i++){
if(maxfromleft<array[i]){
maxfromleft=array[i];
}
int min=maxfromleft>arrayofmax[i+1]?arrayofmax[i+1]:maxfromleft;
if(min>array[i+1]){
ans+=min-array[i+1];
array[i+1]=min;
}
}
System.out.println(ans);
}
}
May be my algorithm is same as above but i think this implementation is easy to understand
Trapping Rain Water problem solved in Java.
class Store
{
static int arr[] = new int[]{0, 1, 0, 2, 2};
// Method for maximum amount of water
static int StoreWater(int n)
{
int max = 0;
int f = 0;
for (int i = 1; i < n; i++)
{
max = Math.max(arr[i], max);
f += Math.max(arr[i], max) - arr[i];
}
return f;
}
public static void main(String[] args)
{
System.out.println("Maximum water that can be accumulated is " +
findWater(arr.length));
}
}
Here is a different and easy approach for water trapping problem. O(1) space and O(N) time complexity.
Logic:
-> Let’s loop from 0 index to the end of the input values.
-> If we find a wall greater than or equal to the previous wall
-> make note of the index of that wall in a var called prev_index
-> keep adding previous wall’s height minus current (ith) wall to the variable water.
-> have a temp variable that also stores the same value as water.
-> Loop till the end, if you dont find any wall greater than or equal to the previous wall, then quit.
-> If the above point is true (i.e, if prev_index < size of input array), then subtract the temp variable from water, and loop from end of the input array to prev_index and find a wall greater than or equal to the previous wall (in this case, the last wall from backwards)
The concept here is if there is a larger wall to the right you can retain water with height equal to the smaller wall on the left.
If there are no larger walls to the right, then start from left. There must be a larger wall to your left now.
You're essentially looping twice, so O(2N), but asymptotically O(N), and of course O(1) space.
JAVA Code Here:
class WaterTrap
{
public static void waterTrappingO1SpaceOnTime(){
int arr[] = {1,2,3,2,1,0}; // answer = 14
int size = arr.length-1;
int prev = arr[0]; //Let first element be stored as previous, we shall loop from index 1
int prev_index = 0; //We need to store previous wall's index
int water = 0;
int temp = 0; //temp will store water until a larger wall is found. If there are no larger walls, we shall delete temp value from water
for(int i=1; i<= size; i++){
if(arr[i] >= prev){ // If current wall is taller then previous wall, make current wall as the previous wall, and its index as previous wall's index for the subsequent loops
prev = arr[i];
prev_index = i;
temp = 0; //because larger or same height wall is found
} else {
water += prev - arr[i]; //Since current wall is shorter then previous, we subtract previous wall height from current wall height and add to water
temp += prev - arr[i]; // Store same value in temp as well, if we dont find larger wall, we will subtract temp from water
}
}
// If the last wall was larger than or equal to the previous wall, then prev_index would be equal to size of the array (last element)
// If we didn't find a wall greater than or equal to the previous wall from the left, then prev_index must be less than index of last element
if(prev_index < size){
water -= temp; //Temp would've stored the water collected from previous largest wall till the end of array if no larger wall was found. So it has excess water. Delete that from 'water' var
prev = arr[size]; // We start from the end of the array, so previous should be assigned to the last element.
for(int i=size; i>= prev_index; i--){ //Loop from end of array up to the 'previous index' which would contain the "largest wall from the left"
if(arr[i] >= prev){ //Right end wall will be definitely smaller than the 'previous index' wall
prev = arr[i];
} else {
water += prev - arr[i];
}
}
}
System.out.println("MAX WATER === " + water);
}
public static void main(String[] args) {
waterTrappingO1SpaceOnTime();
}
}
Algorithm:
1.Create two array left and right of size n. create a variable max_ = INT_MIN.
2.Run one loop from start to end. In each iteration update max_ as max_ = max(max_, arr[i]) and also assign left[i] = max_
3.Update max_ = INT_MIN.
4.Run another loop from end to start. In each iteration update max_ as max_ = max(max_, arr[i]) and also assign right[i] = max_
5.Traverse the array from start to end.
6.The amount of water that will be stored in this column is min(a,b) – array[i],(where a = left[i] and b = right[i]) add this value to total amount of water stored
7.Print the total amount of water stored.
Code:
/*** Theta(n) Time COmplexity ***/
static int trappingRainWater(int ar[],int n)
{
int res=0;
int lmaxArray[]=new int[n];
int rmaxArray[]=new int[n];
lmaxArray[0]=ar[0];
for(int j=1;j<n;j++)
{
lmaxArray[j]=Math.max(lmaxArray[j-1], ar[j]);
}
rmaxArray[n-1]=ar[n-1];
for(int j=n-2;j>=0;j--)
{
rmaxArray[j]=Math.max(rmaxArray[j+1], ar[j]);
}
for(int i=1;i<n-1;i++)
{
res=res+(Math.min(lmaxArray[i], rmaxArray[i])-ar[i]);
}
return res;
}
python code
class Solution:
def trap(self, h: List[int]) -> int:
i=0
j=len(h)-1
ml=-1
mr=-1
left=[]
right=[]
while(i<len(h)):
if ml<h[i]:
ml=h[i]
left.append(ml)
if mr<h[j]:
mr=h[j]
right.insert(0,mr)
i=i+1
j=j-1
s=0
for i in range(len(h)):
s=s+min(left[i],right[i])-h[i]
return s
A drunkard in a grid of streets randomly picks one of four directions and stumbles to the next intersection, then again randomly picks one of four directions, and so on. You might think that on average the drunkard doesn't move very far because the choices cancel each other out, but that is not the case. Represent locations as integer pairs (x,y). Implement the drunkard's walk over 100 intersections, starting at (0,0) and print the ending location
Can anyone help? I'm completely lost with using random generators and loops in the same program
Below is what I have. It complies fine but doesn't print anything and I'm not sure if I got the random 100 intersection thing right
import java.util.*;
class Drunkard {
int x, y;
Drunkard(int x, int y) {
this.x = x;
this.y = y;
}
void moveNorth() {
this.y -= 1;
}
void moveEast() {
this.x += 1;
}
void report() {
System.out.println("Hiccup: " + x + ", " + y);
}
}
class Four {
public static void main(String[] args) {
Random generator = new Random();
Drunkard drunkard = new Drunkard(100, 100);
int direction;
for (int i = 0; i < 100; i++) {
direction = Math.abs(generator.nextInt()) % 4;
if (direction == 0) { // N
drunkard.moveNorth();
} else if (direction == 1) { // E
drunkard.moveEast();
} else if (direction == 2) { // S
System.out.println("Should move South.");
} else if (direction == 3) { // W
System.out.println("Should move West.");
} else {
System.out.println("Impossible!");
}
System.out.drunkard.report();
}
}
}
your program will be:
initialization
loop: for 1 to 100, do:
i = random()
if i<=0.25 : go north
if i>0.25 and i<=0.5 : go south
if i>0.5 and i<= 0.75 : go east
if i>0.75 and i<= 1 : go west
end loop
show final point.
I see a variety of problems:
You are initializing your Drunkard's position to 100,100. The assignment said to initialize to 0,0.
System.out.drunkard.report() absolutely does not compile. Just call drunkard.report().
The instructions say to print the final location, so you need to move the call to drunkard.report() down one line, so that it is outside of the for loop.
You haven't written methods for moveSouth or moveWest. Write them and add calls to them in the appropriate place.
The class Four needs to be public in order to run it directly.
Good Java programming practices say that every class should be in its own file, but this probably goes against what your instructor asked you to do.
But, I don't think that's your problem. I think there's a problem with how/where you're trying to run the program. You say it compiles fine but doesn't print any output. You know that after it compiles there is another step to run the program, right?
To be clear, here's what you should be doing. At a command line, make sure you are in the directory where your .java file lives. I'm going to assume it's called Four.java. Type the following, hitting enter after each line. (Don't type the $ prompt)
$ javac *.java
$ java Four
I copied the code you posted above, fixed the problems I highlighted, and followed my own instructions above; it works perfectly.
You can use
int direction = (new Random()).nextInt(4);
And use this direction variable to determine where he walks to. I would use recursion in this case instead of a loop.
This starts at 0,0. Generates a random number to determine location and updates the location.
Not sure about the way you are generating the random number, this seems to work well for me.
Point currentLocation = new Point();
currentLocation.setLocation(0, 0);
Point newLocation = new Point(0,0);
Random random = new Random();
//make 100 moves
for(int i=0; i<100; i++)
{
int k = random.nextInt(4);
if(k == 0)
{
//use your drunkard method here
newLocation.setLocation(currentLocation.getX(), currentLocation.getY() + 5);
}
else if (k == 1)
{
//go south
newLocation.setLocation(currentLocation.getX(), currentLocation.getY() - 5);
}
else if (k == 2)
{
//go east
newLocation.setLocation(currentLocation.getX() + 5, currentLocation.getY());
}
else if(k == 3)
{
//go west
newLocation.setLocation(currentLocation.getX() - 5, currentLocation.getY());
}
currentLocation.setLocation(newLocation);
}
System.out.println(currentLocation);
}
You're not implementing your random generator to its full extent.
Random generator = new Random();
int direction = generator.nextInt(4); // This will return a random int
// between 0 and 3
Some other useful tricks when using Random() are as follows:
int i = generator.nextInt(4)+2; // This will return a random int
// between 2 and 5
I highly recommend you check out this if you'd really like to learn all of the neat tricks that you can do using the Random Class.
All i did for this is create a loop that generated a random number between -1 and 1, and summed the values 100 times. Do that for x and for y.
int x = 0;
int y = 0;
//intial = (0,0)
//North = (0, 1)
//South = (0, -1)
//East = (1, 0)
//West = (-1, 0)
for(int i = 0; i < 100; i++)
{
x += (int) (Math.random() * 3) + (-1);
y += (int) (Math.random() * 3) + (-1);
}
System.out.printf("The Drunkard is now located at: (%d, %d)", x, y);