Related
In Python, typically to define said game board I would have used the following code:
board = [['0','0','0','0','0','0','0','0','0','0'] for i in range(10)]
However, using Java the only way I have been able to find so far to produce the same result would be to do:
public static String[][] board = {
{"0","0","0","0","0","0","0","0","0","0"},
{"0","0","0","0","0","0","0","0","0","0"},
{"0","0","0","0","0","0","0","0","0","0"},
{"0","0","0","0","0","0","0","0","0","0"},
{"0","0","0","0","0","0","0","0","0","0"},
{"0","0","0","0","0","0","0","0","0","0"},
{"0","0","0","0","0","0","0","0","0","0"},
{"0","0","0","0","0","0","0","0","0","0"},
{"0","0","0","0","0","0","0","0","0","0"},
{"0","0","0","0","0","0","0","0","0","0"}};
Surely there must be a more efficient way of doing this. I know I can do it efficiently if it were to be integers, but it has to be strings given the situation. I had thought a for loop would also have applied, but I can't seem to find the right syntax for it; perhaps it isn't applicable for Java. Are there any suggestions?
You can loop over each row and use Arrays.fill:
public static String[][] board = new String[rows][cols];
static {
for (String[] row : board) Arrays.fill(row, "0");
}
Demo
You can use nested for loops to create the 10 rows and 10 columns. So within the first loop (which creates a "row"), you'd add another loop to fill the "columns."
String[][] board = new String[10][10];
// 10 rows
for (int i = 0; i < 10; i++) {
// 10 columns
for (int j = 0; j < 10; j++) {
// Set the value of this row/column
board[i][j] = "0";
}
}
This obviously isn't as elegant as Python's one-line solution, but it does what you need.
There are other ways to do this (such as just adding a full 10-digit array to the board array in each loop), but I find this to be best for scalability and readability.
For further clarity, if you may possibly need to change the number of rows or columns in the future, it would be better to move those values to variables and use those instead:
int rows = 10;
int cols = 10;
String[][] board = new String[rows][cols];
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; i++) {
board[i][j] = "0";
}
}
This way you only need to update the variables instead of also modifying the for loops and array declaration.
You can do it like this. Since Strings are immutable, it's safe to use clone() here. Thanks to #iota for suggesting setAll
int n = 10;
String[][] arr = new String[n][n];
Arrays.fill(arr[0], "0");
Arrays.setAll(arr, i->arr[0].clone());
for(String[] a : arr) {
System.out.println(Arrays.toString(a));
}
Prints
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
The following is a stream-based solution to the problem that uses a functional style, rather than an iterative style:
return Stream.generate(() ->
Collections.nCopies(10, "0").toArray(String[]::new))
.limit(10)
.toArray(String[][]::new);
Note that depending on the readability, etc. requirements, a classic iterative style may be deemed more desirable.
Explanation
Collections.nCopies(10, "0") creates a List<String> of 10 "0" values. This is converted to a String array using Collection.toArray(IntFunction). This takes an IntFunction which generates an array of a given length. String[]::new is a constructor lambda which implements IntFunction<String[]>, creating an array of the given length.
Stream.generate creates an infinite stream with each element created by a call to a given Supplier. In this case, the supplier creates the aforementioned 10-element long list, resulting in a Stream<String[]>. A generator is used, rather than Collections.nCopies, since each String[] in the String[][] needs be a different object.
The infinite stream is limited to just ten elements using Stream.limit(10).
Finally, the Stream<String[]> is collected into a String[][] by using Stream.toArray(IntFunction), which works in the same manner as the Collection.toArray method already described.
This is a hybrid of for loop and manually creating one row of cells.
String[][] board = new String[10][];
for (int i = 0; i < 10; i++) {
board[i] = new String[]{"0","0","0","0","0","0","0","0","0","0"};
}
String#repeat since 11
String[][] board = new String[10][];
Arrays.setAll - for each row repeat 0 ten times and split into an array:
Arrays.setAll(board, i -> "0".repeat(10).split(""));
Arrays.fill - in this case, the result is the same:
Arrays.fill(board, "0".repeat(10).split(""));
Output:
Arrays.stream(board).map(Arrays::toString).forEach(System.out::println);
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
See also: How can I fill this 2D array with X characters?
Essentially I am making a board game using sockets. Each client connected is stored in a map .
When the user makes a move to a coordinate in the 2D Array i.e. "move 2,3", it should put the connectionID in that position.
Currently my issue is, because I have a for loop; when i use the move command, the connection ID is replaced with the last value in the loop.
public void move(int x, int y) {
for (int value : gs.returnClients().values()) {
storeArray[x][y] = value;
}
}
i.e. If i have 2 clients connected: {62029=1, 62032=2} and my board
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 2, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
Let's say I want to have client 1 move to 0, 3 it should be:
[[0, 0, 0, 1, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 2, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
But instead I get:
[[0, 0, 0, 2, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 2, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
Since it's overwriting the last value of the map in the for loop.
How would I make it move dependent on its client "id" from the map?
Edit
In my server i have clients.put(socket.getPort(), connectionID); and methods to return the map and id. I have a class GameService that executes command which is
switch (request.type) {
case MOVE:
int clientID = Integer.parseInt(request.params[0]);
int getX = Integer.parseInt(request.params[1]);
int getY = Integer.parseInt(request.params[2]);
game.move(clientID,getX, getY);
return game.returnBoard();
In a file Request
String[] items = line.trim().split("\\s+");
switch (items[0].toUpperCase()) {
case "MOVE":
return new Request(RequestType.MOVE, items[1], items[2], items[3]);
As far as I understand your code, you need to do your move the following way:
public void move(int client, int x, int y) {
storeArray[x][y] = client;
}
where client is one (!) of the numbers within the gs.returnClients().values()-list. If you need to take it out of that map, you need to deliver the appropriate key for that, e.g.:
public void move(int clientId, int x, int y) {
storeArray[x][y] = gs.returnClients().get(clientId);
}
I have a string in the below format.
[[115, 1, 0123490, 63824005632, 0036760004, , 01, N, 78, , 7481067028,
122016, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
TABORA, EMMANUEL, J, 4732 WENATCHIE TRL, LIMA, OH, 45805, EM, RXRELIEF CARD,
MUCINEX DM 20 0056-32 TAB SA 12HR 600-30MG], [115, 1, 0123490,
63824005632, 0038380001, ,
01, N, 78, , 7481067028, 122016, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, TABORA, EMMANUEL, J, 4732 WENATCHIE TRL, LIMA,
OH, 45805, EM, APEX AFFINITY DISCOUNT CARD, MUCINEX DM 20 0056-32 TAB SA
12HR 600-30MG]]
I want to store in collection with each
[115, 1, 0123490, 63824005632, 0038380001, , 01, N, 78, , 7481067028,
122016, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
TABORA, EMMANUEL, J, 4732 WENATCHIE TRL, LIMA, OH, 45805, EM, APEX AFFINITY
DISCOUNT CARD, MUCINEX DM 20 0056-32 TAB SA 12HR 600-30MG]
[115, 1, 0123490, 63824005632, 0036760004, , 01, N, 78, , 7481067028,
122016, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
TABORA, EMMANUEL, J, 4732 WENATCHIE TRL, LIMA, OH, 45805, EM, RXRELIEF CARD,
MUCINEX DM 20 0056-32 TAB SA 12HR 600-30MG]
How can I split or store in collection?
This should work as long the character ] does not appear as part of a value inside an entry:
public static void main(String[] args) {
String clob = "[[115, 1, 0123490, 63824005632, 0036760004, , 01, N, 78, , 7481067028, 122016, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, TABORA, EMMANUEL, J, 4732 WENATCHIE TRL, LIMA, OH, 45805, EM, RXRELIEF CARD, MUCINEX DM 20 0056-32 TAB SA 12HR 600-30MG], [115, 1, 0123490, 63824005632, 0038380001, , 01, N, 78, , 7481067028, 122016, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, TABORA, EMMANUEL, J, 4732 WENATCHIE TRL, LIMA, OH, 45805, EM, APEX AFFINITY DISCOUNT CARD, MUCINEX DM 20 0056-32 TAB SA 12HR 600-30MG]]";
List<String> entries = new ArrayList<>();
int start = 1;
while (true) {
start = clob.indexOf("[", start);
int end = clob.indexOf("]", start);
if (start != -1 && end != -1) {
entries.add(clob.substring(start, end + 1));
start = end + 1;
} else {
break;
}
}
}
If you know the escape sequence for characters inside your entries (e.g. \]) you have to check if the found end index represents that escape sequence and if, read again, starting from the end index.
There are many ways to do it. Here’s my suggestion:
public static List<List<String>> stringTo2DList(String input) {
if (input.equals("[]")) {
return Collections.emptyList();
}
if (! input.startsWith("[[")) {
throw new IllegalArgumentException("Not a list of lists");
}
if (! input.endsWith("]]")) {
throw new IllegalArgumentException("Not a list of lists");
}
List<List<String>> result = new ArrayList<>();
String[] innerLists = input.substring(2, input.length() - 2).split("\\], \\[");
for (String innerList : innerLists) {
// check for empty inner list
if (innerList.isEmpty()) {
result.add(Collections.emptyList());
} else {
result.add(Arrays.asList(innerList.split(", ")));
}
}
return result;
}
Should your string contain [], I am interpreting it as an empty list even though it might be a list of one element, the empty string. If you prefer the latter, just skip the check for the empty list in for loop.
I've been following a tutorial about creating a basic tile-based tower defense game in Java and have encountered a piece of code I cannot wrap my brain around and would like some help. (My main question is at the bottom after the code)
At this point we are iterating through a multidimensional array of 0's and 1's that we pass to a constructor which has a method that assigns a grass tile for 0's and stone tile for 1's and then another method to draw them to the screen creating our game screen. Simple enough, right?
Here is the class:
package data;
import static helpers.Artist.*;
public class TileGrid {
public Tile[][] map;
public TileGrid(int[][] newMap){
map = new Tile[20][15];
for(int i = 0; i < map.length; i++){
for(int j = 0; j <map[i].length; j++){
switch(newMap[j][i]){
case 0:
map[i][j] = new Tile(row * 64, col * 64, 64, 64, TileType.GRASS);
break;
case 1:
map[i][j] = new Tile(row * 64, col * 64, 64, 64, TileType.STONE);
break;
case 2:
map[i][j] = new Tile(row * 64, col * 64, 64, 64, TileType.WATER);
break;
}
}
}
}
public void Draw(){
for(int i = 0; i < map.length; i++){
for(int j = 0; j < map[i].length; j++){
Tile t = map[i][j];
DrawQuadTex(t.getTexture(), t.getX(), t.getY(), t.getWidth(), t.getHeight());
}
}
}
}
And here is the array we are passing in:
int[][] map = { //20 tiles wide, 15 tiles high
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
{0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0},
{0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0},
};
So my main question has to do with the switch statement in the constructor for the TileGrid.
Why do the i and the j get switched newMap[j][i] when checking what it equals? I get that this code works, well, because it does and I understand nested for loops to iterate through a multidimensional array.
But why wouldn't newMap[i][j] work?
As well at the very beginning of that same constructor why do we create an array (of type Tile) with the dimension of map = new Tile[20][15] when we are passing in an array with the dimensions of map[15][20]?
I have tried to figure this out and study this on my own and will continue to do so until I understand it but any help would be soooo appreciated!
You are passing to the TileGrid constructor a 2D array of 15 rows and 20 columns, but inside the constructor you create a 2D array of 20 rows and 15 columns. That's why map[i][j] corresponds with newMap[j][i].
If the input int[][] map was also of 20 rows and 15 columns, you wouldn't have to switch the order of the indices inside the constructor.
This would give an index out of bounds error if it was really switched. However, there is a col variable in the second for loop that is not defined anywhere so it's hard to tell what's going on. This code will not compile.
If it is some code pulled off a a tutorial on some web page, I would just assume that it was a typo and continue on with that in mind. OR better yet, contact the author.
I have data stored in files at paths such as:
/home/yamada/data/train/atheism/file_name.txt
I use this data to populate a hash map, storing the origin of the data and its contents as follows.
/home/yamada/data/test/sports/t.s_1.txt, [0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0]
/home/yamada/data/test/politics/t.p_0.txt, [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0]
/home/yamada/data/test/atheism/t.a_0.txt, [0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
/home/yamada/data/test/science/t.s_0.txt, [1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 2, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0]
However, I want only to store the path to the point of the directory, not the specific file, like so:
/home/yamada/data/train/atheism
The following regex command is capable of extracting the component I'm interested in according to regex101.com:
(home\/yamada\/data\/train\/atheism)
How could I use the java pattern matcher to ensure that only the string mentioned earlier, the path up to and including the directory, but not the file name, is saved to the hash map?
Is the pattern matcher the best choice for this operation?
Below is the method that populates the hash map.
public static void perceptron_data_struc_generateur(Set<String> GLOBO_DICT,
Map<File, ArrayList<String> > fileDict,
Map<File, int[] > perceptron_input)
{
//create a new entry in the array list 'perceptron_input'
//with the key as the file name from fileDict
//create a new array which is the length of GLOBO_DICT
//iterate through the indicies of GLOBO_DICT
//for all words in globo dict, if that word appears in fileDict,
//increment the perceptron_input index that corresponds to that
//word in GLOBO_DICT by the number of times that word appears in fileDict
//so i can get the index later
List<String> GLOBO_DICT_list = new ArrayList<>(GLOBO_DICT);
for (Map.Entry<File, ArrayList<String>> entry : fileDict.entrySet())
{
int[] cross_czech = new int[GLOBO_DICT_list.size()];
//initialize to zero
Arrays.fill(cross_czech, 0);
for (String s : GLOBO_DICT_list)
{
for(String st : entry.getValue())
{
if( st.equals(s) )
{
cross_czech[ GLOBO_DICT_list.indexOf( s ) ] = cross_czech[ GLOBO_DICT_list.indexOf( s ) ] +1;
}
}
}
perceptron_input.put( entry.getKey() , cross_czech);
}
}
It's quite a bit simpler than that:
String dir = filename.replaceAll("/[^/]*$", "");
If I understand your question correctly you want to find only parts which ends with / (file name will not have it). In that case
(\w+/)+
should do the trick (BTW we don't escape / in Java's regex)
But if your data is always in form path/to/file and you only want to extract path/to then you don't need regex, you can use File class and its getParent method like
String data = new File("/home/yamada/data/train/atheism/file_name.txt").getParent();
System.out.println(data);
This will return \home\yamada\data\train\atheism so you will have / instead of \, but this shouldn't be a problem if you want to use this data in Java (File accepts both separators).