Solving crosswords efficiently [closed] - java

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
The community reviewed whether to reopen this question 10 months ago and left it closed:
Original close reason(s) were not resolved
Improve this question
I have a crossword puzzle and a list of words which can be used to solve it (words can be placed multiple times or not even once). There is always a solution for the given crossword and word list.
I searched for clues on how to solve this problem and found out that it is NP-Complete. My maximal crossword size is 250 by 250, the maximal length of the list (amount of words which can be used to solve it) is 200. My goal is to solve crosswords of this size by brute force/backtracking, which should be possible within a few seconds (this is a rough estimation by me, correct me if I am wrong).
For example:
A list of given words which can be used to solve the crossword:
can
music
tuna
hi
The given empty crossword (X are fields which cannot be filled out, the empty fields need to be filled):
The solution:
Now my current approach is to represent the crossword as a 2-D array and search for empty spaces (2 iterations over the crossword). Then I match words to empty spaces depending on their length, then I try all combinations of words to empty spaces which have the same length. This approach got very messy very fast, I got lost trying to implement this, is there a more elegant solution?

The basic idea you have is pretty sensible:
Identify slots on the board.
Try each slot with each word that fits.
If every slots can be filled without conflict, it is solved.
It's an excellent plan.
The next step is to translate it into a design.
For small program like this we can go straight to pseudo code.
The gist of it, as explained by other answers, is recursion:
1 Draw a slot from the slot pool.
2 If slot pool is empty (all slots filled), stop solving.
3 For each word with correct length:
4 If part of the slot is filled, check conflict.
5 If the word does not fit, continue the loop to next word.
// No conflict
6 Fill the slot with the word.
// Try next slot (down a level)
7 Recur from step 1.
8 If the recur found no solution, revert (take the word back) and try next.
// None of them works
9 If no words yield a solution, an upper level need to try another word.
Revert (put the slot back) and go back.
Below is a short but complete example that I cooked up from your requirements.
There is more than one way to skin a cat.
My code swapped step 1 and 2, and combines step 4 to 6 in one fill loop.
Key points:
Use a formatter to fit the code to your style.
The 2D board is stored in a linear character array in row-major order.
This allow the board to be save by clone() and restored by arraycopy.
On creation, the board is scanned for slots in two passes from two directions.
The two slot lists are solved by the same loop, differ mainly in how the slots are filled.
The recur process is displayed, so you can see how it works.
Many assumptions are made. No single letter slot, all words in same case, board is correct etc.
Be patient. Learn whatever is new and give yourself time to absorb it.
Source:
import java.awt.Point;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Supplier;
import java.util.stream.Stream;
public class Crossword {
public static void main ( String[] args ) {
new Crossword( Arrays.asList( "5 4 4\n#_#_#\n_____\n#_##_\n#_##_\ntuna\nmusic\ncan\nhi".split( "\n" ) ) );
new Crossword( Arrays.asList( "6 6 4\n##_###\n#____#\n___#__\n#_##_#\n#____#\n##_###\nnice\npain\npal\nid".split( "\n" ) ) );
}
private final int height, width; // Board size
private final char[] board; // Current board state. _ is unfilled. # is blocked. other characters are filled.
private final Set<String> words; // List of words
private final Map<Point, Integer> vertical = new HashMap<>(), horizontal = new HashMap<>(); // Vertical and horizontal slots
private String indent = ""; // For formatting log
private void log ( String message, Object... args ) { System.out.println( indent + String.format( message, args ) ); }
private Crossword ( List<String> lines ) {
// Parse input data
final int[] sizes = Stream.of( lines.get(0).split( "\\s+" ) ).mapToInt( Integer::parseInt ).toArray();
width = sizes[0]; height = sizes[1];
board = String.join( "", lines.subList( 1, height+1 ) ).toCharArray();
words = new HashSet<>( lines.subList( height+1, lines.size() ) );
// Find horizontal slots then vertical slots
for ( int y = 0, size ; y < height ; y++ )
for ( int x = 0 ; x < width-1 ; x++ )
if ( isSpace( x, y ) && isSpace( x+1, y ) ) {
for ( size = 2 ; x+size < width && isSpace( x+size, y ) ; size++ ); // Find slot size
horizontal.put( new Point( x, y ), size );
x += size; // Skip past this horizontal slot
}
for ( int x = 0, size ; x < width ; x++ )
for ( int y = 0 ; y < height-1 ; y++ )
if ( isSpace( x, y ) && isSpace( x, y+1 ) ) {
for ( size = 2 ; y+size < height && isSpace( x, y+size ) ; size++ ); // Find slot size
vertical.put( new Point( x, y ), size );
y += size; // Skip past this vertical slot
}
log( "A " + width + "x" + height + " board, " + vertical.size() + " vertical, " + horizontal.size() + " horizontal." );
// Solve the crossword, horizontal first then vertical
final boolean solved = solveHorizontal();
// Show board, either fully filled or totally empty.
for ( int i = 0 ; i < board.length ; i++ ) {
if ( i % width == 0 ) System.out.println();
System.out.print( board[i] );
}
System.out.println( solved ? "\n" : "\nNo solution found\n" );
}
// Helper functions to check or set board cell
private char get ( int x, int y ) { return board[ y * width + x ]; }
private void set ( int x, int y, char character ) { board[ y * width + x ] = character; }
private boolean isSpace ( int x, int y ) { return get( x, y ) == '_'; }
// Fit all horizontal slots, when success move to solve vertical.
private boolean solveHorizontal () {
return solve( horizontal, this::fitHorizontal, "horizontally", this::solveVertical );
}
// Fit all vertical slots, report success when done
private boolean solveVertical () {
return solve( vertical, this::fitVertical, "vertically", () -> true );
}
// Recur each slot, try every word in a loop. When all slots of this kind are filled successfully, run next stage.
private boolean solve ( Map<Point, Integer> slot, BiFunction<Point, String, Boolean> fill, String dir, Supplier<Boolean> next ) {
if ( slot.isEmpty() ) return next.get(); // If finished, move to next stage.
final Point pos = slot.keySet().iterator().next();
final int size = slot.remove( pos );
final char[] state = board.clone();
/* Try each word */ indent += " ";
for ( String word : words ) {
if ( word.length() != size ) continue;
/* If the word fit, recur. If recur success, done! */ log( "Trying %s %s at %d,%d", word, dir, pos.x, pos.y );
if ( fill.apply( pos, word ) && solve( slot, fill, dir, next ) )
return true;
/* Doesn't match. Restore board and try next word */ log( "%s failed %s at %d,%d", word, dir, pos.x, pos.y );
System.arraycopy( state, 0, board, 0, board.length );
}
/* No match. Restore slot and report failure */ indent = indent.substring( 0, indent.length() - 2 );
slot.put( pos, size );
return false;
}
// Try fit a word to a slot. Return false if there is a conflict.
private boolean fitHorizontal ( Point pos, String word ) {
final int x = pos.x, y = pos.y;
for ( int i = 0 ; i < word.length() ; i++ ) {
if ( ! isSpace( x+i, y ) && get( x+i, y ) != word.charAt( i ) ) return false; // Conflict
set( x+i, y, word.charAt( i ) );
}
return true;
}
private boolean fitVertical ( Point pos, String word ) {
final int x = pos.x, y = pos.y;
for ( int i = 0 ; i < word.length() ; i++ ) {
if ( ! isSpace( x, y+i ) && get( x, y+i ) != word.charAt( i ) ) return false; // Conflict
set( x, y+i, word.charAt( i ) );
}
return true;
}
}
Exercise: You can rewrite recursion to iteration; faster and can support bigger boards.
Once that's done it can be converted to multi-thread and run even faster.

You are right the problem is NP-complete. So your best chance is to solve it by brute-force (if you find a polynomial algorithm please tell me, we can both be rich =)).
What I suggest you is to take a look at backtracking. It will allow you to write an elegant (and yet slow given your input size) solution to the crossword problem.
If you need more inspirational material take a look at this solver that uses backtracking as a method to navigate the solution tree.
Note that there are algorithms out there that might in practice perform better than a pure brute-force (even though still of exponential complexity).
Also, a quick search on scholar reveals a good number of papers on the topic that you might want to take a look at, such as the followings:
using genetic algorithm
using a probabilistic approach

A crossword puzzle is a Constraint satisfaction problem which is generally a NP-Complete, but there are many solvers that will apply the most efficient algorithms to a constraint problem that you specify. The Z3 SMT solver can solve these problems very easily and at scale. All you have to do is write a Java program that transforms the crossword puzzle into a SMT problem the solver can understand then gives it to the solver to solve it. Z3 has Java bindings so it should be pretty simple. I have written the Z3 code for solving the first example below. It should not be difficult for you to follow the pattern in your Java program to specify arbitrarily large crossroad puzzles.
; Declare each possible word as string literals
(define-const str1 String "tuna")
(define-const str2 String "music")
(define-const str3 String "can")
(define-const str4 String "hi")
; Define a function that returns true if the given String is equal to one of the possible words defined above.
(define-fun validString ((s String)) Bool
(or (= s str1) (or (= s str2) (or (= s str3) (= s str4)))))
; Declare the strings that need to be solved
(declare-const unknownStr1 String)
(declare-const unknownStr2 String)
(declare-const unknownStr3 String)
(declare-const unknownStr4 String)
; Assert the correct lengths for each of the unknown strings.
(assert (= (str.len unknownStr1) 4))
(assert (= (str.len unknownStr2) 5))
(assert (= (str.len unknownStr3) 3))
(assert (= (str.len unknownStr4) 2))
; Assert each of the unknown strings is one of the possible words.
(assert (validString unknownStr1))
(assert (validString unknownStr2))
(assert (validString unknownStr3))
(assert (validString unknownStr4))
; Where one word in the crossword puzzle intersects another assert that the characters at the intersection point are equal.
(assert (= (str.at unknownStr1 1) (str.at unknownStr2 1)))
(assert (= (str.at unknownStr2 3) (str.at unknownStr4 1)))
(assert (= (str.at unknownStr2 4) (str.at unknownStr3 0)))
; Solve the model
(check-sat)
(get-model)
I recommend the Z3 SMT solver, but there are plenty of other constraint solvers. There is no need for you to implement your own constraint solving algorithm any more than there is a need for you to implement your own sorting algorithm.

To make this problem easier to solve, I'll break this down into smaller, easier problems. Note that I am not including code/algorithms, as I believe that will not help here (If we wanted the best Code, there would be indexes and databases and black magic that makes your head explode just seeing it). Instead, this answer tries to answer the question by talking about methods of thought that will help the OP tackle this problem (and future ones) using the method that works best for the reader.
What you need to know
This answer assumes you know how to do the following
Create and use Objects that have properties and functions
Pick a data structure that works (not necessarily good) for what you want to do with its contents.
Modeling your space
So, it's easy enough to load your crossword into an n by m matrix (2D array, hereby 'grid'), but this is very heard to work with pragmatically. So lets start by parsing your crossword from a grid to a legitimate object.
As far as your program needs to know, each entry in the crossword has 4 properties.
An X-Y coordinate in the grid for the first letter
A direction (down or across)
Word length
Word value
Map of bound indexes
Key: Index of word that is shared with another entry
Value: Entry that index is shared with
(You can make this a tuple and include the shared index from the other entry for easy refrence)
You can find these in the grid based on these rules while scanning.
If Row_1_up is closed and Row_1_down is open, this is the start index of a down word. (scan down for for length. For bound indexes, left or right space will be open. scan left to get linked entry coord-id)
Same as 1 but rotated for across words (You can do this at the same time as the scan for 1)
In your crossword object, you can store the entries using the coordinate+direction as the key for easy reference and easy conversion to/from text grid form.
Using your model
You should now have an object containing a collection of crossword entries, which contain their relevant index bindings. You now need to find a set of values that will satisfy all your entries.
Your entry objects should have helper methods like isValidEntry(str) that checks for the given value, and the current state of the crossword, can I put this word here? By making each object in your model responsible for its own level of logic, the code for the problem one thought layer up can just call the logic without worrying about it's implementation (in this example, Your solver doesn't have to worry about the logic of is a value valid, it can just ask isValidEntry for that)
If you have done the above right, solving the problem is then a simple matter of iterating over all words for all entries to find a solution.
List of sub problems
For reference, here is my list of sub problems that you need to write something to solve.
How can I ideally model my work-space that is easy for me to work with?
For each piece of my model, what does it need to know? What logic can it handle for me?
How can I transform my text input into a usable model object?
How do I solve my problem using my model objects? (For you, it is iterate all words/all entries to find a valid set. Maybe using recursion)

I just implemented a code in Scala to solve such puzzles. I am just using recursion to solve the problem. In short, for each word, I find all possible slots, and pick a slot and fill it with the word, and try to solve the partial puzzle with recursion. If the puzzle cannot be filled with the rest of words, it tries another slot, etc. if not, the puzzle is solved.
Here is the link to my code:
https://github.com/mysilver/AMP/blob/master/Crossword.scala

Related

Optimizing item order in an ArrayList

I have an ArrayList of colors and their frequency of appearance. My program should calculate a reordering of those items that maximizes the minimum distance between two equal bricks.
For example, given input consisting of 4*brick 1 (x), 3*brick 2 (y), and 5*brick 3 (z), one correct output would be: z y x z x z y x z x y.
My code does not produce good solutions. In particular, sometimes there are 2 equal bricks at the end, which is the worst case.
import java.util.ArrayList;
import java.util.Collections;
public class Calc {
// private ArrayList<Wimpel> w = new ArrayList<Brick>();
private String bKette = "";
public String bestOrder(ArrayList<Brick> w) {
while (!w.isEmpty()) {
if (w.get(0).getFrequency() > 0) {
bChain += w.get(0).getColor() + "|";
Brick brick = new Wimpel(w.get(0).getVariant(), w.get(0).getFrequency() - 1);
w.remove(0);
w.add(brick);
// bestOrder(w);
} else {
w.remove(0);
}
bestOrder(w);
}
return bOrder;
}
public int Solutions(ArrayList<Wimpel> w) {
ArrayList<Brick> tmp = new ArrayList<Brick>(w);
int l = 1;
int counter = (int) w.stream().filter(c -> Collections.max(tmp).getFrequency() == c.getFrequency()).count();
l = (int) (fakultaet(counter) * fakultaet((tmp.size() - counter)));
return l;
}
public static long fakultaet(int n) {
return n == 0 ? 1 : n * fakultaet(n - 1);
}
}
How can make my code choose an optimal order?
We will not perform your exercise for you, but we will give you some advice.
Consider your current approach: it operates by filling the result string by cycling through the bricks, choosing one item from each brick in turn as long as any items remain in that brick. But this approach is certain to fail when one brick contains at least two items more than any other, because then only that brick remains at the end, and all its remaining items have to be inserted one after the other.
That is, the problem is not that your code is buggy per se, but rather that your whole strategy is incorrect for the problem. You need something different.
Now, consider the problem itself. Which items will appear at the shortest distance apart in a correct ordering? Those having the highest frequency, of course. And you can compute that minimum distance based on the frequency and total number of items.
Suppose you arrange these most-constrained items first, at the known best distance.
What's left to do at this point? Well, you potentially have some more bricks with lesser frequency, and some more slots in which to accommodate their items. If you ignore the occupied slots altogether, you can treat this as a smaller version of the same problem you had before.

Recursive backtracking in Java for solving a crossword

I need to solve a crossword given the initial grid and the words (words can be used more than once or not at all).
The initial grid looks like that:
++_+++
+____+
___+__
+_++_+
+____+
++_+++
Here is an example word list:
pain
nice
pal
id
The task is to fill the placeholders (horizontal or vertical having length > 1) like that:
++p+++
+pain+
pal+id
+i++c+
+nice+
++d+++
Any correct solution is acceptable, and it's guaranteed that there's a solution.
In order to start to solve the problem, I store the grid in 2-dim. char array and I store the words by their length in the list of sets: List<Set<String>> words, so that e.g. the words of length 4 could be accessed by words.get(4)
Then I extract the location of all placeholders from the grid and add them to the list (stack) of placeholders:
class Placeholder {
int x, y; //coordinates
int l; // the length
boolean h; //horizontal or not
public Placeholder(int x, int y, int l, boolean h) {
this.x = x;
this.y = y;
this.l = l;
this.h = h;
}
}
The main part of the algorithm is the solve() method:
char[][] solve (char[][] c, Stack<Placeholder> placeholders) {
if (placeholders.isEmpty())
return c;
Placeholder pl = placeholders.pop();
for (String word : words.get(pl.l)) {
char[][] possibleC = fill(c, word, pl); // description below
if (possibleC != null) {
char[][] ret = solve(possibleC, placeholders);
if (ret != null)
return ret;
}
}
return null;
}
Function fill(c, word, pl) just returns a new crossword with the current word written on the current placeholder pl. If word is incompatible with pl, then function returns null.
char[][] fill (char[][] c, String word, Placeholder pl) {
if (pl.h) {
for (int i = pl.x; i < pl.x + pl.l; i++)
if (c[pl.y][i] != '_' && c[pl.y][i] != word.charAt(i - pl.x))
return null;
for (int i = pl.x; i < pl.x + pl.l; i++)
c[pl.y][i] = word.charAt(i - pl.x);
return c;
} else {
for (int i = pl.y; i < pl.y + pl.l; i++)
if (c[i][pl.x] != '_' && c[i][pl.x] != word.charAt(i - pl.y))
return null;
for (int i = pl.y; i < pl.y + pl.l; i++)
c[i][pl.x] = word.charAt(i - pl.y);
return c;
}
}
Here is the full code on Rextester.
The problem is that my backtracking algorithm doesn't work well. Let's say this is my initial grid:
++++++
+____+
++++_+
++++_+
++++_+
++++++
And this is the list of words:
pain
nice
My algorithm will put the word pain vertically, but then when realizing that it was a wrong choice it will backtrack, but by that time the initial grid will be already changed and the number of placeholders will be reduced. How do you think the algorithm can be fixed?
This can be solved in 2 ways:
Create a deep copy of the matrix at the start of fill, modify and return that (leaving the original intact).
Given that you already pass around the matrix, this wouldn't require any other changes.
This is simple but fairly inefficient as it requires copying the matrix every time you try to fill in a word.
Create an unfill method, which reverts the changes made in fill, to be called at the end of each for loop iteration.
for (String word : words.get(pl.l)) {
if (fill(c, word, pl)) {
...
unfill(c, word, pl);
}
}
Note: I changed fill a bit as per my note below.
Of course just trying to erase all letter may erase letters of other placed words. To fix this, we can keep a count of how many words each letter is a part of.
More specifically, have a int[][] counts (which will also need to be passed around or be otherwise accessible) and whenever you update c[x][y], also increment counts[x][y]. To revert a placement, decrease the count of each letter in that placement by 1 and only remove letters with a count of 0.
This is somewhat more complex, but much more efficient than the above approach.
In terms of code, you might put something like this in fill:
(in the first part, the second is similar)
for (int i = pl.x; i < pl.x + pl.l; i++)
counts[pl.y][i]++;
And unfill would look something like this: (again for just the first part)
for (int i = pl.x; i < pl.x + pl.l; i++)
counts[pl.y][i]--;
for (int i = pl.x; i < pl.x + pl.l; i++)
if (counts[pl.y][i] == 0)
c[pl.y][i] = '_';
// can also just use a single loop with "if (--counts[pl.y][i] == 0)"
Note that, if going for the second approach above, it might make more sense to simply have fill return a boolean (true if successful) and just pass c down to the recursive call of solve. unfill can return void, since it can't fail, unless you have a bug.
There is only a single array that you're passing around in your code, all you're doing is changing its name.
See also Is Java "pass-by-reference" or "pass-by-value"?
You identified it yourself:
it will backtrack, but by that time the initial grid will be already
changed
That grid should be a local matrix, not a global one. That way, when you back up with a return of null, the grid from the parent call is still intact, ready to try the next word in the for loop.
Your termination logic is correct: when you find a solution, immediately pass that grid back up the stack.

How to hard-code legal moves for fast lookup?

I have created a gameboard (5x5) and I now want to decide when a move is legal as fast as possible. For example a piece at (0,0) wants to go to (1,1), is that legal? First I tried to find this out with computations but that seemed bothersome. I would like to hard-code the possible moves based on a position on the board and then iterate through all the possible moves to see if they match the destinations of the piece. I have problems getting this on paper. This is what I would like:
//game piece is at 0,0 now, decide if 1,1 is legal
Point destination = new Point(1,1);
destination.findIn(legalMoves[0][0]);
The first problem I face is that I don't know how to put a list of possible moves in an array at for example index [0][0]. This must be fairly obvious but I am stuck at this for some time. I would like to create an array in which there is a list of Point objects. So in semi-code: legalMoves[0][0] = {Point(1,1),Point(0,1),Point(1,0)}
I am not sure if this is efficient but it makes logically move sense than maybe [[1,1],[0,1],[1,0]] but I am not sold on this.
The second problem I have is that instead of creating the object at every start of the game with an instance variable legalMoves, I would rather have it read from disk. I think that it should be quicker this way? Is the serializable class the way to go?
My 3rd small problem is that for the 25 positions the legal moves are unbalanced. Some have 8 possible legal moves, others have 3. Maybe this is not a problem at all.
You are looking for a structure that will give you the candidate for a given point, i.e. Point -> List<Point>.
Typically, I would go for a Map<Point, List<Point>>.
You can initialise this structure statically at program start or dynamically when needing. For instance, here I use 2 helpers arrays that contains the possible translations from a point, and these will yield the neighbours of the point.
// (-1 1) (0 1) (1 1)
// (-1 0) (----) (1 0)
// (-1 -1) (0 -1) (1 -1)
// from (1 0) anti-clockwise:
static int[] xOffset = {1,1,0,-1,-1,-1,0,1};
static int[] yOffset = {0,1,1,1,0,-1,-1,-1};
The following Map contains the actual neighbours for a Point with a function that compute, store and return these neighbours. You can choose to initialise all neighbours in one pass, but given the small numbers, I would not think this a problem performance wise.
static Map<Point, List<Point>> neighbours = new HashMap<>();
static List<Point> getNeighbours(Point a) {
List<Point> nb = neighbours.get(a);
if (nb == null) {
nb = new ArrayList<>(xOffset.length); // size the list
for (int i=0; i < xOffset.length; i++) {
int x = a.getX() + xOffset[i];
int y = a.getY() + yOffset[i];
if (x>=0 && y>=0 && x < 5 && y < 5) {
nb.add(new Point(x, y));
}
}
neighbours.put(a, nb);
}
return nb;
}
Now checking a legal move is a matter of finding the point in the neighbours:
static boolean isLegalMove(Point from, Point to) {
boolean legal = false;
for (Point p : getNeighbours(from)) {
if (p.equals(to)) {
legal = true;
break;
}
}
return legal;
}
Note: the class Point must define equals() and hashCode() for the map to behave as expected.
The first problem I face is that I don't know how to put a list of possible moves in an array at for example index [0][0]
Since the board is 2D, and the number of legal moves could generally be more than one, you would end up with a 3D data structure:
Point legalMoves[][][] = new legalMoves[5][5][];
legalMoves[0][0] = new Point[] {Point(1,1),Point(0,1),Point(1,0)};
instead of creating the object at every start of the game with an instance variable legalMoves, I would rather have it read from disk. I think that it should be quicker this way? Is the serializable class the way to go?
This cannot be answered without profiling. I cannot imagine that computing legal moves of any kind for a 5x5 board could be so intense computationally as to justify any kind of additional I/O operation.
for the 25 positions the legal moves are unbalanced. Some have 8 possible legal moves, others have 3. Maybe this is not a problem at all.
This can be handled nicely with a 3D "jagged array" described above, so it is not a problem at all.

Light/Ray casting in java?

I've been trying to make a dynamic light system in java, without using libraries. For some reason, though, it seems I can't get light to run efficiently. It flickers and lags a ton. I'm doing this with no previous understanding of lighting engines in games, so I'm open to suggestions. Here is my current update method:
public void updateLight( ArrayList<Block> blocks )
{
//reset light
light.reset();
//add the x and y of this light
light.addPoint( x, y );
//precision for loops
int ires = 1;
int jres = 2;
for( int i = 0; i < width; i += ires )
{
//get radians of current angle
float rdir = (float)Math.toRadians( dir + i - width/2 );
//set up pixel vars
int px, py;
for( int j = 0; j < length; j += jres )
{
//get position of pixel
px = (int)ZZmath.getVectorX( x, rdir, j );
py = (int)ZZmath.getVectorY( y, rdir, j );
//if point gets found
boolean foundpoint = false;
for( int n = 0; n < blocks.size(); n ++ )
{
//check if block is solid
//also check that collision is possible really quickly for efficiency
if( blocks.get( n ).solid )
{
//get info on block
int bx = blocks.get( n ).x;
int by = blocks.get( n ).y;
//quick trim
if( Math.abs( bx - px ) <= 32 && Math.abs( by - py ) <= 32 )
{
int bw = blocks.get( n ).w;
int bh = blocks.get( n ).h;
if( ZZmath.pointInBounds( px, py, bx, by, bw, bh ) )
{
//add point to polygon
light.addPoint( px, py );
//found point
foundpoint = true;
}
}
}
}
//if a point is found, break
if( foundpoint )
{
break;
}
//if at end of loop, add point
//loose definition of "end" to prevent flickers
if( j >= length - jres*2 )
{
light.addPoint( px, py );
}
}
}
}
This modifies a polygon that displays for light. I'll change that later. Any idea of ways I can make this run better? Also, no, no libraries. I don't have anything against them, just don't want to use one now.
You implementation doesn't appear to use much of the stuff I see here:
http://www.cs.utah.edu/~shirley/books/fcg2/rt.pdf
I'd recommend digesting this completely. If your objective is to understand ray tracing deeply, that's how it should be done.
Maybe your objective was to learn by writing your own raytracer. In my experience I would end up rewriting this code several times and still not get it completely right. It's good to get your hands dirty but it's not necessarily the most effective way to go about things.
Overall it looks like you need to study (object oriented) programming concepts, and take a data structures and algorithms course.
The biggest thing is readability. Document your code, for your future self if no one else. This means Clear comments before and during updateLight(). The existing comments are alright (though they paraphrase the code more than justify it), but "really quickly for efficiency" is a lie.
For a small issue of readability that could be a tiny drag on performance, make a local variable for blocks.get(n). Name it something short but descriptive, save typing and only make one method call to retrieve it.
"if at end of loop": I have no idea which loop you mean, and the for loops have definite ends. A comment }//end for or }//end for width is often helpful.
Checking if the block is solid is unnecessary! Just store your blocks in two lists, and only go through the solid blocks. Even if you have some desire to have flickering blocks, one remove and add is cheaper than O(width*length*numbernotsolid) extra work.
There are many ways you could structure how the blocks are stored to facilitate quick testing. You only want or need to test blocks whose coordinates are near to a particular light. The basic strategy is divide the space into a grid, and sort the blocks based on what section of the grid they fall into. Then when you have light in a particular section of the grid, you know you only need to test blocks in that section (and possibly a neighboring section - there are details depending on their width and the light's).
I have no idea whether that is along the lines of the right approach or not. I don't know much about raytracing, although it is or used to be rather slow. It looks like you have a decent naive implementation. There might be a slightly different naive approach that is faster and some more difficult (to code to completion) algorithms that are moderately yet more fast.
Also, I see no need to do this breadth first. Why not solve for one line (you call them pixels?) at a time. Count the number of times this code calls Math.toRadians. It looks like it's just an extraneous line because you could work along the same angle until ready for the next.

Game State Tree for Golf Solitaire

I am working on a homework project in which we need to generate a state tree for the game of Golf Solitaire. To do this I decided to create a Move class which represents a move from one stack of cards to another - simply put, it holds a reference to the two stacks involved in a single move. Each node in my tree knows what it's move is.
The problem I am currently having is that my trees are much too large - like OutOfMemoryError too large. Edit - I should note that when I make a tree for a very small game I do not get an error, but my tree is still much larger than it should be!
For those unfamiliar with the game: Golf Solitaire Rules. Note - We did not need to implement the restriction on playing a Queen after a King.
public void makeTree()
{
_root = makeNode( null, null, 0 );
}
private Node makeNode( Node parent, Move m, int depth )
{
Node node = new Node( parent, m, depth );
ArrayList<Move> moves = findAllMoves();
if( moves.size() == 0 )
node.setScore( getScore() );
else {
for( Move mv : moves ) {
mv.makeMove();
Node child = makeNode( node, mv, depth++ );
node.addChild( child );
mv.undoMove();
}
}
return node;
}
private ArrayList<Move> findAllMoves()
{
ArrayList<Move> moves = new ArrayList<Move>();
for( int i = 0; i < numPlayPiles; i++ ) {
if( _play[i].size() != 0 ) {
if( Math.abs( _play[i].top().getRank().ordinal() -
discard.getRank().ordinal() ) == 1 ) {
moves.add( new Move( _play[i], _discard ) );
}
}
}
if( _draw.size() != 0 )
moves.add( new Move( _draw, _discard ) );
return moves;
}
_play[i] is simply referencing a pile of cards in the game.
I realize this is a bit much of code to be posting in a question, but my question is simply about my logic. I have a suspicion that my error is in how I am returning makeNode yet I am totally stuck here. Could anybody confirm my suspicion and/or possibly give me some guidance?
For anybody who is curious, my issue was one of not reading the assignment properly. Specifically, my findAllMoves method was the issue. A node should have either a move from the draw pile or all possible moves from play piles, but not both. My code above allows each child, even at the same depth, to each create a Move from the draw pile to discard pile - resulting in a huge amount of duplicate nodes.
It also never checks the end-game conditions. As a result, each leaf node was either a losing score, or 0 (the method would recurse until the draw pile was empty even if the player already won).

Categories