0

I am trying to develop a game in android that solves the boggle. At this stage i have hard-coded the 4x4 array values.

It has an activity and 2 java helper class. The problem is that when i run the java helper classes in console mode it solves the boggle perfectly. But, when i integrate in android environment it throws an error "Unfortunately, Wordgame has stopped" and stops.

The activity code is as follows:

package org.example.sudoku;

import java.io.IOException;
import java.util.HashSet;
import java.util.Iterator;

import android.app.Activity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;

public class GameActivity extends Activity {

    public final static char matrix[][] = 
        { 
                {'s', 'h', 'g', 'n'},
                {'u', 'e', 'o', 'o'},
                {'s', 'l', 'o', 'h'},
                {'k', 'l', 'd', 'p'}
        };


    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_game);

        ListView list = (ListView) findViewById(R.id.list);
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
                android.R.layout.simple_list_item_1);


        Solver solver = null;
        try {
            solver = new Solver();
        } catch (IOException e) {
            e.printStackTrace();
        }

        HashSet<String> words;
        words = solver.findWords(matrix, true); // The programs throws error in this line

        //DO SOMETHING WITH THE VALUE OF "words"
    }

}

Solver.java Class is as follow:

package org.example.sudoku;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashSet;

public class Solver {
    private boolean q_is_qu_flag;
    private Trie st_machine;
    private HashSet<String> words_;

    public Solver() throws IOException {
        q_is_qu_flag = true;
        st_machine = new Trie();
        words_ = new HashSet<String>();

        // insert dictionary words
        FileInputStream fis = new FileInputStream("wordlist.txt");
        BufferedReader br = new BufferedReader(new InputStreamReader(fis, "UTF-8"));
        String word;
        while ((word = br.readLine()) != null) {
            st_machine.insert(word);
        }

        br.close();
    }

private void check(char matrix[][], boolean path[][], int i, int j) {
    if (i < 0 || i >= matrix.length || j < 0 || j >= matrix[0].length)
        return;

    // check if it's gonna form a cycle
    if (path[i][j] == true)
        return;

    if (!st_machine.transition_forward(matrix[i][j]))
                        return;

                // special case 'q' becomes 'qu'
                // additional transition forward
                if (matrix[i][j] == 'q' && q_is_qu_flag) {
                        if (!st_machine.transition_forward('u'))
                                return;
                }

                // update path
                path[i][j] = true;

                if (st_machine.isWord())
                        words_.add(st_machine.path());

                check(matrix, path, i-1, j-1);
                check(matrix, path,   i, j-1);
                check(matrix, path, i+1, j-1);
                check(matrix, path, i-1,   j);
                check(matrix, path, i+1,   j);
                check(matrix, path, i-1, j+1);
                check(matrix, path,   i, j+1);
                check(matrix, path, i+1, j+1);

                st_machine.transition_backward();

                // pop 'q' after popping 'u'
                if (matrix[i][j] == 'q' && q_is_qu_flag)
                        st_machine.transition_backward();

                // pop off path
                path[i][j] = false;
        }

        // Returns dictionary words found in the 2d array
        // 'q' is treated as 'qu' by default
        // call the overloaded method if you want 'q' to be treated as is
        public HashSet<String> findWords(char matrix[][]) {
                // path is used to determine cycles
                boolean path[][] = new boolean[matrix.length][matrix[0].length];
                for (int i = 0; i < matrix.length; i++) {
                        for (int j = 0; j < matrix[0].length; j++) {
                                check(matrix, path, i, j);
                        }
                }
                return words_;
        }

        // First param: 2D array of characters
        // Second param: Boolean value. If true, 'q' is treated as 'qu' else 'q' is treated as is.
        // Returns dictionary words found in the 2d array
        public HashSet<String> findWords(char matrix[][], boolean qIsQu) {
                q_is_qu_flag = qIsQu;
                return findWords(matrix);
        }
}

Trie.java Class is as follow:

package org.example.sudoku;

import java.util.Stack;

public class Trie {

        private class State {
                boolean isWord;
                State alphabet[];

                private State() {
                        isWord = false;
                        alphabet = new State[26]; 
                }
        }

        private State start_;
        private Stack<State> state_stack;
        private Stack<Character> path_;

        public Trie() {
                start_ = new State();
                state_stack = new Stack<State>();
                state_stack.push(start_);
                path_ = new Stack<Character>();
        }

        private int charToInt(char c) {
                return (int)Character.toUpperCase(c) - 65;
        }

        public void insert(State state, String word, int str_index) {
                if (str_index == word.length()) {
                        state.isWord = true;
                        return;
                }
                int index = charToInt(word.charAt(str_index));
                if (index < 0 || index > 25)
                        return;
                if (state.alphabet[index] == null)
                        state.alphabet[index] = new State(); 
                insert(state.alphabet[index], word, str_index+1);
        }

        public void insert(String word) {
                word = word.toUpperCase();
                this.insert(start_, word, 0);
        }

        public boolean transition_forward(char c) {
                int transition_input = charToInt(c);
                State curr_state = state_stack.peek();
                if (curr_state.alphabet[transition_input] == null)
                        return false;
                state_stack.push(curr_state.alphabet[transition_input]);
                path_.push(c);
                return true;
        }

        public boolean transition_backward() {
                if (state_stack.size() > 1) {
                        state_stack.pop();
                        path_.pop();
                        return true;
                }
                return false;
        }

        public boolean isWord() {
                return state_stack.peek().isWord;
        }

        public String path() {
                char path[] = new char[path_.size()];
                for (int i=0; i < path_.size(); i++)
                        path[i] = path_.get(i).charValue();
                return new String(path);
        }
}

AndroidManifest.xml file is as follow:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="org.example.sudoku"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="18" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="org.example.sudoku.GameActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>        
    </application>

</manifest>

The logcat output is as follow:

11-14 02:09:54.511: D/gralloc_goldfish(1088): Emulator without GPU emulation detected.
11-14 02:11:57.461: I/Choreographer(1088): Skipped 33 frames!  The application may be doing too much work on its main thread.
11-14 02:11:57.941: D/dalvikvm(1088): GC_FOR_ALLOC freed 67K, 7% free 2818K/3004K, paused 70ms, total 85ms
11-14 02:11:57.961: I/dalvikvm-heap(1088): Grow heap (frag case) to 3.471MB for 635812-byte allocation
11-14 02:11:58.081: D/dalvikvm(1088): GC_FOR_ALLOC freed 7K, 6% free 3432K/3628K, paused 112ms, total 112ms
11-14 02:11:58.251: W/System.err(1088): java.io.FileNotFoundException: /wordlist.txt: open failed: ENOENT (No such file or directory)
11-14 02:11:58.251: W/System.err(1088):     at libcore.io.IoBridge.open(IoBridge.java:409)
11-14 02:11:58.251: W/System.err(1088):     at java.io.FileInputStream.<init>(FileInputStream.java:78)
11-14 02:11:58.251: W/System.err(1088):     at java.io.FileInputStream.<init>(FileInputStream.java:105)
11-14 02:11:58.281: W/System.err(1088):     at org.example.sudoku.Solver.<init>(Solver.java:20)
11-14 02:11:58.281: W/System.err(1088):     at org.example.sudoku.GameActivity.onCreate(GameActivity.java:36)
11-14 02:11:58.281: W/System.err(1088):     at android.app.Activity.performCreate(Activity.java:5133)
11-14 02:11:58.301: W/System.err(1088):     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
11-14 02:11:58.301: W/System.err(1088):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2175)
11-14 02:11:58.301: W/System.err(1088):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2261)
11-14 02:11:58.321: W/System.err(1088):     at android.app.ActivityThread.access$600(ActivityThread.java:141)
11-14 02:11:58.331: W/System.err(1088):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1256)
11-14 02:11:58.331: W/System.err(1088):     at android.os.Handler.dispatchMessage(Handler.java:99)
11-14 02:11:58.341: W/System.err(1088):     at android.os.Looper.loop(Looper.java:137)
11-14 02:11:58.341: W/System.err(1088):     at android.app.ActivityThread.main(ActivityThread.java:5103)
11-14 02:11:58.351: W/System.err(1088):     at java.lang.reflect.Method.invokeNative(Native Method)
11-14 02:11:58.351: W/System.err(1088):     at java.lang.reflect.Method.invoke(Method.java:525)
11-14 02:11:58.351: W/System.err(1088):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:737)
11-14 02:11:58.361: W/System.err(1088):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
11-14 02:11:58.371: W/System.err(1088):     at dalvik.system.NativeStart.main(Native Method)
11-14 02:11:58.381: W/System.err(1088): Caused by: libcore.io.ErrnoException: open failed: ENOENT (No such file or directory)
11-14 02:11:58.411: W/System.err(1088):     at libcore.io.Posix.open(Native Method)
11-14 02:11:58.421: W/System.err(1088):     at libcore.io.BlockGuardOs.open(BlockGuardOs.java:110)
11-14 02:11:58.432: W/System.err(1088):     at libcore.io.IoBridge.open(IoBridge.java:393)
11-14 02:11:58.432: W/System.err(1088):     ... 18 more
11-14 02:11:58.451: D/AndroidRuntime(1088): Shutting down VM
11-14 02:11:58.451: W/dalvikvm(1088): threadid=1: thread exiting with uncaught exception (group=0x414c4700)
11-14 02:11:58.521: E/AndroidRuntime(1088): FATAL EXCEPTION: main
11-14 02:11:58.521: E/AndroidRuntime(1088): java.lang.RuntimeException: Unable to start activity ComponentInfo{org.example.sudoku/org.example.sudoku.GameActivity}: java.lang.NullPointerException
11-14 02:11:58.521: E/AndroidRuntime(1088):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2211)
11-14 02:11:58.521: E/AndroidRuntime(1088):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2261)
11-14 02:11:58.521: E/AndroidRuntime(1088):     at android.app.ActivityThread.access$600(ActivityThread.java:141)
11-14 02:11:58.521: E/AndroidRuntime(1088):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1256)
11-14 02:11:58.521: E/AndroidRuntime(1088):     at android.os.Handler.dispatchMessage(Handler.java:99)
11-14 02:11:58.521: E/AndroidRuntime(1088):     at android.os.Looper.loop(Looper.java:137)
11-14 02:11:58.521: E/AndroidRuntime(1088):     at android.app.ActivityThread.main(ActivityThread.java:5103)
11-14 02:11:58.521: E/AndroidRuntime(1088):     at java.lang.reflect.Method.invokeNative(Native Method)
11-14 02:11:58.521: E/AndroidRuntime(1088):     at java.lang.reflect.Method.invoke(Method.java:525)
11-14 02:11:58.521: E/AndroidRuntime(1088):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:737)
11-14 02:11:58.521: E/AndroidRuntime(1088):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
11-14 02:11:58.521: E/AndroidRuntime(1088):     at dalvik.system.NativeStart.main(Native Method)
11-14 02:11:58.521: E/AndroidRuntime(1088): Caused by: java.lang.NullPointerException
11-14 02:11:58.521: E/AndroidRuntime(1088):     at org.example.sudoku.GameActivity.onCreate(GameActivity.java:42)
11-14 02:11:58.521: E/AndroidRuntime(1088):     at android.app.Activity.performCreate(Activity.java:5133)
11-14 02:11:58.521: E/AndroidRuntime(1088):     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
11-14 02:11:58.521: E/AndroidRuntime(1088):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2175)
11-14 02:11:58.521: E/AndroidRuntime(1088):     ... 11 more
11-14 02:16:58.691: I/Process(1088): Sending signal. PID: 1088 SIG: 9

In the above circumstances, I would urge upon the experts to suggest me the solution to the above error.

Guy Coder
  • 24,501
  • 8
  • 71
  • 136
mbk
  • 41
  • 4

3 Answers3

1

You can't open files in Android like you can do it in the JVM on PC. You should place your file to the your_project/res/raw directory and then you can access it via R.raw.your_file.

Your code to open the file will look like following:

BufferedReader instream = new BufferedReader(new InputStreamReader(
                context.getResources().openRawResource(R.raw.your_file)));
Victor Nidens
  • 474
  • 1
  • 4
  • 11
  • Thanks for your reply. I have solved the file access issue. But, now i have another issue. I am having OutOfMemoryError. I would highly appreciate your help in this regard. – mbk Nov 14 '13 at 09:09
  • Does it happen when you read the content of the file? Show the code which you have now and we can look through it. – Victor Nidens Nov 14 '13 at 09:21
  • Can you please give me your email so that i can send you the file?? Because, StackOverFlow is not allowing me to add code here. – mbk Nov 14 '13 at 09:44
  • You can post the code on the [pastebin](http://pastebin.com) and put the link here – Victor Nidens Nov 14 '13 at 10:23
  • The updated GameActivity.java is in http://pastebin.com/63eTaKtN, logcat output is in http://pastebin.com/U0hzB25U. Please note that the wordlist.txt file contains more than 400K dictionary words, one word in each line. It is throwing OutOfMemoryError. Seeking your help in this regard. – mbk Nov 14 '13 at 10:43
  • The amount of memory available for the app is limited and depends on the device. So if you have so many data then you have to load only some of them to memory. (see [this question](http://stackoverflow.com/questions/4351678/two-questions-about-max-heap-sizes-and-available-memory-in-android)) What you can do: you can load (e.g.) a batch of 50k words and do what you want with them then and load another 50k batch of words (instead of previous batch) and continue to do it until you go through the whole dictionary. Sure you have to adjust the amount of words in the batch to fit in the memory. – Victor Nidens Nov 14 '13 at 10:53
  • But, is it possible to create the complete trie using all the words at runtime. Because, partial trie is useless. – mbk Nov 14 '13 at 11:11
  • I wanted to see the code where you load words from the file. It's not listed in your pastebins. What the size of your dictionary file? – Victor Nidens Nov 14 '13 at 11:21
  • The code is actually loaded in the Solver.java class, [which can be seen here](http://pastebin.com/JU8aBKy8). Another required class is [Trie.java](http://pastebin.com/zRW1csZ0). The dictionary is a text file that has 432334 words, one word per line (File size is 4.77 MB). It cannot be pasted in pastebin due to oversize issue. – mbk Nov 15 '13 at 01:24
  • If we assume that average word length is 6 chars then the String object requires up to 80 bytes storage (2 byte for each char because default encoding is UTF8 and some space for object related info). Also HashSet requires additional space for array with hashes and its overwhelming per object is about 36 bytes. So we have about 116 bytes of memory per object. For 432334 words it's about 49Mb of memory. Also your app needs memory for its interface and etc. You can try to force the ASCII encoding for words and save 1 byte per char and maybe it can help you on some devices but it's not a good way. – Victor Nidens Nov 15 '13 at 08:03
  • Thanks for the comment. Can you suggest me the optimization technique to applied here? – mbk Nov 15 '13 at 09:33
  • Use ASCII encoding for Strings (`public String(byte[] bytes, String charsetName)`). Load words from you dictionary by small batches. That's all. – Victor Nidens Nov 15 '13 at 11:04
0

Application is not able to get file wordlist.txt from the location you have mentioned. Try getting the file from where it is stored.

Rohit5k2
  • 17,948
  • 8
  • 45
  • 57
0

First of all ignoring exception is bad. In your code, if Solver() throws exception you print the exception and continue as if it didn't occur.

You can use:

    try {
        solver = new Solver();
        HashSet<String> words;
        words = solver.findWords(matrix, true); 
    } catch (IOException e) {
        e.printStackTrace();
    }

The real issue is that it cannot find the file at the expected place.

BobTheBuilder
  • 18,858
  • 6
  • 40
  • 61