AuD
Lecture 'Algorithmen und Datenstrukturen' (code examples)
Grid.java
Go to the documentation of this file.
1package aud.example.grid;
2
3import java.util.Comparator;
4import java.io.*;
5
6import aud.Stack;
7import aud.Queue;
8import aud.PriorityQueue;
9import aud.HashMap;
10import aud.util.Terminal;
11
33public class Grid {
34
35 int num_rows_ = 0;
36 int num_columns_ = 0;
37 Cell[] cell_ = null;
38 int num_opened_ = 0;
39 boolean show_parent_ = false;
40 boolean ansi_term_ = true;
41 int frame_ = 0;
42
44 public final static String EXAMPLE =
45 "+-+ +----- --------+ +----+\n"+
46 "| | +------+----- ---+ | |\n"+
47 "| | A | | | |\n"+
48 "| | +------+----- ---+ | |\n"+
49 "+-+ | +----+\n"+
50 " +----- --------+\n"+
51 "\n"+
52 " +\n";
53
55 public int delay = 100;
56
71 public class Cell {
73 public char value = '\000';
75 public boolean is_free() {
76 return value=='\000' || value=='.' || value=='*' ||
77 Character.isWhitespace(value) ||
78 Character.isLetterOrDigit(value);
79 }
81 public boolean is_tunnel() { return 'a'<=value && value<='z'; }
82
84 public int color = Terminal.WHITE;
86 public double d = Double.POSITIVE_INFINITY;
88 public double f = Double.POSITIVE_INFINITY;
90 public int p = -1;
91 };
92
94 class CompareCellDistanceFromIndex implements Comparator<Integer> {
95 Grid grid;
96 CompareCellDistanceFromIndex(Grid g) { grid=g; }
97 @Override public int compare(Integer a,Integer b) {
98 Cell ca=grid.cell_[a.intValue()], cb=grid.cell_[b.intValue()];
99 double d=ca.d-cb.d ;
100 return (d<0.0) ? -1 : (d>0.0 ? +1 : 0);
101 }
102 };
104 class CompareCellEstimatedDistanceFromIndex implements Comparator<Integer> {
105 Grid grid;
106 CompareCellEstimatedDistanceFromIndex(Grid g) { grid=g; }
107 @Override public int compare(Integer a,Integer b) {
108 Cell ca=grid.cell_[a.intValue()], cb=grid.cell_[b.intValue()];
109 double d=ca.f-cb.f ;
110 return (d<0.0) ? -1 : (d>0.0 ? +1 : 0);
111 }
112 };
113
115 public Grid() {}
116
118 public Grid(String filename) {
119 read_file(filename);
120 }
121
123 public void read_file(String filename) {
124 StringBuilder sb=new StringBuilder();
125 try {
126 FileReader fr=new FileReader(filename);
127 BufferedReader in=new BufferedReader(fr);
128 String line;
129 while ((line=in.readLine())!= null) {
130 sb.append(line);
131 sb.append("\n");
132 }
133 } catch (Exception e) {
134 System.err.println("error while reading '"+filename+"'");
135 System.err.println(e.toString());
136 System.exit(-1);
137 }
138 setup(sb.toString());
139 }
140
142 public void setup(String[] lines) {
143 num_rows_=lines.length;
144 num_columns_=0; // find maximum number of columns
145 for (String line : lines) {
146 num_columns_=(num_columns_<line.length() ? line.length() : num_columns_);
147 }
148
149 cell_=new Cell[size()];
150 for (int i=0;i<size();++i)
151 cell_[i]=new Cell();
152
153 int i=0;
154 for (String line : lines) {
155 for (int j=0;j<line.length();++j) {
156 cell(i,j).value=line.charAt(j);
157 }
158 ++i;
159 }
160 }
162 public void setup(String text) {
163 setup(text.split("\\n"));
164 }
165
167 public int num_rows() { return num_rows_; }
169 public int num_columns() { return num_columns_; }
171 public int size() { return num_rows_*num_columns_; }
172
174 public int cell_index(int i,int j) {
175 assert(0<=i && i<num_rows_);
176 assert(0<=j && j<num_columns_);
177 return i*num_columns_+j;
178 }
180 public int get_row(int _index) {
181 return _index/num_columns_;
182 }
184 public int get_column(int _index) {
185 return _index%num_columns_;
186 }
188 public Cell cell(int i,int j) {
189 return cell_[cell_index(i,j)];
190 }
192 public int[] neighbors(int index) {
193 int i=get_row(index);
194 int j=get_column(index);
195 int north=-1,east=-1,south=-1,west=-1, k=0;
196
197 // if (i>0) { north=cell_index(i-1,j); ++k; }
198 // if (j<num_columns_-1) { east =cell_index(i,j+1); ++k; }
199 // if (i<num_rows_-1) { south=cell_index(i+1,j); ++k; }
200 // if (j>0) { west =cell_index(i,j-1); ++k; }
201
202 // equivalent but more efficient:
203 if (i>0) { north=index-num_columns_; ++k; }
204 if (j<num_columns_-1) { east =index+1; ++k; }
205 if (i<num_rows_-1) { south=index+num_columns_; ++k; }
206 if (j>0) { west =index-1; ++k; }
207
208 int[] nhd=new int[k];
209 k=0;
210
211 int[] tunnel_nhd = neighbors_from_tunnels(index);
212 if (tunnel_nhd.length > 1) {
213 // copy tunnels to nhd, but filter this cell index
214 nhd=new int[nhd.length + tunnel_nhd.length-1];
215 for (int n=0;n<tunnel_nhd.length;++n) {
216 if (tunnel_nhd[n] != index) {
217 nhd[k++] = tunnel_nhd[n];
218 }
219 }
220 }
221
222 if (north>=0) nhd[k++]=north;
223 if (east>=0) nhd[k++]=east;
224 if (south>=0) nhd[k++]=south;
225 if (west>=0) nhd[k++]=west;
226
227 return nhd;
228 }
229
230 private aud.HashMap<Character,int[]> tunnels_ = null;
231
233 protected int[] neighbors_from_tunnels(int index) {
234 Cell cell = cell_[index];
235
236 if (!cell.is_tunnel())
237 return new int[0];
238
239 if (tunnels_ == null)
240 tunnels_ = new aud.HashMap<Character,int[]>();
241
242 int[] nhd = tunnels_.find(cell.value);
243
244 if (nhd == null) {
246 }
247
248 return nhd;
249 }
250
252 protected int[] create_tunnels_for(Cell cell) {
253 assert cell.is_tunnel() : "expect tunnel entry";
254 // count ...
255 int n=0;
256 for (int i=0;i<cell_.length;++i) {
257 if (cell_[i].value == cell.value)
258 ++n;
259 }
260 int[] nhd = new int[n];
261
262 // ... save
263 tunnels_.insert(cell.value, nhd);
264
265 // .. and copy
266 n=0;
267 for (int i=0;i<cell_.length;++i) {
268 if (cell_[i].value == cell.value)
269 nhd[n++] = i;
270 }
271 return nhd;
272 }
273
291 double weight(int s,int t) {
292 char cs=cell_[s].value, ct=cell_[t].value;
293
294 double s_level=Character.isDigit(cs) ? Character.getNumericValue(cs) : 0.0;
295 double t_level=Character.isDigit(ct) ? Character.getNumericValue(ct) : 0.0;
296
297 return Math.abs(t_level-s_level)+1.0;
298 }
299
303 public int find_first(char value) {
304 for (int i=0;i<size();++i)
305 if (cell_[i].value==value)
306 return i;
307
308 Terminal.instance.reset();
309 System.err.println("Could not find a cell with value='"+value+"'.");
310 System.exit(-1);
311
312 return -1;
313 }
314
315
317 public void display() {
319
320 term.cls(frame_++==0);
321 term.fg(Terminal.BLACK);
322 term.hideCursor();
323
324 for (int i=0;i<num_rows();++i) {
325
326 for (int j=0;j<num_columns();++j) {
327 Cell c=cell(i,j);
328
329 term.bg(c.is_free() ? c.color : Terminal.BLUE);
330
331 char ch=c.value=='\000' ? ' ' : c.value;
332
333 // display characters for colors
334 if (term.dumb() && ch==' ') {
335 switch (c.color) {
336 case Terminal.RED: ch='&'; break;
337 case Terminal.BLUE: ch='@'; break;
338 case Terminal.GREEN: ch='$'; break;
339 case Terminal.YELLOW: ch='~'; break;
340 case Terminal.CYAN: ch='*'; break;
341 case Terminal.MAGENTA: ch='.'; break;
342 case Terminal.BLACK: ch='%'; break;
343 case Terminal.WHITE:
344 default: break;
345 }
346 }
347 else if (show_parent_) {
348 if (i>0 && c.p==cell_index(i-1,j)) ch='↓';
349 else if (i<num_rows_ && c.p==cell_index(i+1,j)) ch='↑';
350 else if (j>0 && c.p==cell_index(i,j-1)) ch='→';
351 else if (j<num_columns_ && c.p==cell_index(i,j+1)) ch='←';
352 }
353
354 term.out.print(ch);
355 }
356 term.out.println();
357 }
358 term.showCursor();
359 term.reset();
360
361 term.out.flush();
362 try {
363 Thread.sleep(delay);
364 } catch (InterruptedException e) {}
365 }
366
368 public void reset_cells() {
369 for (int i=0;i<size();++i) {
370 Cell c=cell_[i];
371 cell_[i]=new Cell();
372 cell_[i].value=c.value;
373 }
374 }
375
377 public void dfs_recursive(int s) {
378 cell_[s].color=Terminal.CYAN; // start processing s0
379 cell_[s].d=0.0;
380 display();
381 for (int t : neighbors(s)) {
382 if (cell_[t].color==Terminal.WHITE && cell_[t].is_free()) {
383
384 cell_[t].d=cell_[s].d+weight(s,t);
385 cell_[t].p=s;
386
387 dfs_recursive(t);
388 }
389 }
390 cell_[s].color=Terminal.MAGENTA; // done with s0
391 display();
392 }
393
395 public void dfs_iterative(int s0) {
397
398 open.push(s0);
399 cell_[s0].color=Terminal.CYAN; // start processing
400 cell_[s0].d=0.0;
401 display();
402
403 while (!open.is_empty()) {
404 int s=open.pop();
405
406 for (int t : neighbors(s)) {
407 if (cell_[t].color==Terminal.WHITE && cell_[t].is_free()) {
408
409 cell_[t].color=Terminal.CYAN; // start processing
410 cell_[t].d=cell_[s].d+weight(s,t);
411 cell_[t].p=s;
412
413 open.push(t);
414 }
415 }
416 cell_[s].color=Terminal.MAGENTA;
417 display();
418 }
419 }
420
422 public void bfs(int s0) {
424
425 open.enqueue(s0);
426 cell_[s0].color=Terminal.CYAN; // start processing
427 cell_[s0].d=0.0;
428 display();
429
430 while (!open.is_empty()) {
431 int s=open.dequeue();
432
433 for (int t : neighbors(s)) {
434 if (cell_[t].color==Terminal.WHITE && cell_[t].is_free()) {
435
436 cell_[t].color=Terminal.CYAN; // start processing
437 cell_[t].d=cell_[s].d+weight(s,t);
438 cell_[t].p=s;
439
440 open.enqueue(t);
441 }
442 }
443 cell_[s].color=Terminal.MAGENTA;
444 display();
445 }
446 }
447
449 public void dijkstra(int s0,int s1) {
450
452 new PriorityQueue<Integer>(size(),new CompareCellDistanceFromIndex(this));
453
454 open.push(s0);
455 ++num_opened_;
456 cell_[s0].color=Terminal.CYAN; // start processing
457 cell_[s0].d=0.0;
458 display();
459
460 while (!open.is_empty()) {
461 int s=open.pop();
462
463 for (int t : neighbors(s))
464 if (cell_[t].is_free()) {
465
466 double priority=cell_[s].d+weight(s,t);
467
468 if (Double.isInfinite(cell_[t].d)) {
469
470 assert(!open.contains(t));
471
472 cell_[t].color=Terminal.CYAN; // start processing
473 cell_[t].p=s;
474 cell_[t].d=priority;
475
476 open.push(t);
477 ++num_opened_;
478 }
479 else if (cell_[t].d>priority && open.contains(t)) {
480 cell_[t].color=Terminal.YELLOW; // got updated
481 cell_[t].p=s;
482 cell_[t].d=priority;
483
484 open.lower(t); // adapt priority queue to new, lower priority
485 }
486 }
487
488 cell_[s].color=Terminal.MAGENTA;
489 display();
490
491 if (s==s1) {
492 backtrack(s);
493 break;
494 }
495 }
496 }
497
499 double estimate_distance(int s,int t) {
500 int dx=get_column(s)-get_column(t);
501 int dy=get_row(s)-get_row(t);
502
503 return Math.abs((double) dx)+Math.abs((double) dy);
504 }
505
507 public void astar(int s0,int s1) {
508
511 (size(),new CompareCellEstimatedDistanceFromIndex(this));
512
513 open.push(s0);
514 ++num_opened_;
515 cell_[s0].color=Terminal.CYAN; // start processing
516 cell_[s0].d=0.0;
517 cell_[s0].f=estimate_distance(s0,s1); // initial guess
518 display();
519
520 while (!open.is_empty()) {
521 int s=open.pop();
522
523 for (int t : neighbors(s))
524 if (cell_[t].is_free()) {
525
526 double distance=cell_[s].d+weight(s,t);
527
528 double priority = distance + estimate_distance(t,s1);
529
530 if (Double.isInfinite(cell_[t].d)) {
531
532 assert(!open.contains(t));
533
534 cell_[t].color=Terminal.CYAN; // start processing
535 cell_[t].p=s;
536 cell_[t].d=distance;
537 cell_[t].f=priority;
538
539 open.push(t);
540 ++num_opened_;
541 }
542 else if (cell_[t].d>distance && open.contains(t)) {
543 cell_[t].color=Terminal.YELLOW; // got updated
544 cell_[t].p=s;
545 cell_[t].d=distance;
546 cell_[t].f=priority;
547
548 open.lower(t); // adapt priority queue to new, lower priority
549 }
550 }
551
552 cell_[s].color=Terminal.MAGENTA;
553 display();
554
555 if (s==s1) {
556 backtrack(s);
557 break;
558 }
559 }
560 }
561
563 public void backtrack(int s) {
564 assert(cell_[s].p>=0);
565 double d=cell_[s].d;
566 while (s>=0) {
567 cell_[s].color=Terminal.GREEN;
568 s=cell_[s].p;
569 display();
570 }
571 Terminal.instance.out.println("distance = "+d);
572 if (num_opened_>0)
573 Terminal.instance.out.println("examined "+num_opened_+" cells in total");
574 }
575
577 protected static void usage() {
578 System.err.println
579 ("java aud.example.graph.TraversalExample "+
580 "[-g file] [-m method] [-s s0] [-d s1] [-v N]\n"+
581 " -g FILE read grid from FILE\n"+
582 " -m METHOD traversal {dfs,idfs,bfs,dijkstra,astar}\n"+
583 " -p show parent cell by an arrow (requires color)\n"+
584 " -s START start node (one character, default: 'A')\n"+
585 " -d DESTINATION destination node for A* (default: none)\n"+
586 " -delay MILLISECONDS set delay after each step\n"+
587 " -no-color don't use ANSI codes\n"+
588 " -color force use of ANSI codes (requires terminal emulation)\n"+
589 " -show display initial grid and exit"
590 );
591 System.exit(-1);
592 }
593
594 public static void main(String[] args) {
595
596 Runtime.getRuntime().addShutdownHook(new Thread() {
597 @Override public void run() {
598 // make sure we are leaving cleanly on SIGINT
599 Terminal.instance.reset();
600 Terminal.instance.showCursor();
601 Terminal.instance.out.flush();
602 }
603 });
604
605
606 Grid g=new Grid();
607
608 // prcess argumentys
609 String method="dfs";
610 char start='A', destination='A';
611
612 for (int i=0;i<args.length;++i) {
613 if (args[i].compareTo("-g")==0) {
614 g.read_file(args[++i]);
615 if (g.size()==0) {
616 System.err.println("error: grid is empty");
617 System.exit(-1);
618 }
619 }
620 else if (args[i].compareTo("-m")==0)
621 method=args[++i];
622 else if (args[i].compareTo("-s")==0)
623 start=args[++i].charAt(0);
624 else if (args[i].compareTo("-d")==0)
625 destination=args[++i].charAt(0);
626 else if (args[i].compareTo("-delay")==0)
627 g.delay=Integer.parseInt(args[++i]);
628 else if (args[i].compareTo("-p")==0)
629 g.show_parent_=true;
630 else if (args[i].compareTo("-no-color")==0)
631 Terminal.instance=new Terminal(false);
632 else if (args[i].compareTo("-color")==0)
633 Terminal.instance=new Terminal(true);
634 else if (args[i].compareTo("-show")==0)
635 method=null;
636 else
637 usage();
638 }
639
640 if (g.size()==0)
641 g.setup(EXAMPLE); // use default grid
642
643 g.display();
644
645 if (method==null) // show only
646 System.exit(0);
647
648 // find source node (or exit)
649 int s0=g.find_first(start);
650
651 // find destination node if given (or exit)
652 int s1=(destination!=start ? g.find_first(destination) : -1);
653
654 if (method.compareTo("dfs")==0)
655 g.dfs_recursive(s0);
656 else if (method.compareTo("idfs")==0)
657 g.dfs_iterative(s0);
658 else if (method.compareTo("bfs")==0)
659 g.bfs(s0);
660 else if (method.compareTo("dijkstra")==0)
661 g.dijkstra(s0,s1);
662 else if (method.compareTo("astar")==0) {
663 if (s1<0) {
664 System.err.println("A* requires destination, use '-d'");
665 System.exit(-1);
666 }
667 g.astar(s0,s1);
668 }
669 else
670 usage();
671
672 }
673}
Implementation of an unordered map based on a hash table.
Definition: HashMap.java:31
Value find(Key key)
find value for key @endiliteral
Definition: HashMap.java:281
Priority queue based on binary min-heap.
boolean is_empty()
Is PQ empty?
void push(T x)
Add entry to PQ.
boolean contains(T x)
Is x contained in PQ?
T pop()
Pop minimal element from PQ.
void lower(T x)
update heap after lowering priority of x
Implementation of AbstractQueue as a (dynamically resized) circular buffer based on array.
Definition: Queue.java:11
void enqueue(T x)
Enqueue element at end of queue.
Definition: Queue.java:62
boolean is_empty()
Is queue empty?
Definition: Queue.java:35
T dequeue()
Remove front element from queue.
Definition: Queue.java:50
Implementation of a stack based on aud.Vector.
Definition: Stack.java:8
void push(T x)
Push x onto stack.
Definition: Stack.java:31
boolean is_empty()
Is stack empty?
Definition: Stack.java:14
T pop()
Pop element from stack.
Definition: Stack.java:23
Cell in a Grid.
Definition: Grid.java:71
double f
priority used by A* (distance + estimated distance to destination
Definition: Grid.java:88
char value
the value that is displayed
Definition: Grid.java:73
int p
index of parent cell
Definition: Grid.java:90
boolean is_tunnel()
letters a-z denote entries to tunnels: e.g., all 'a' are connected
Definition: Grid.java:81
boolean is_free()
Can we enter this cell?
Definition: Grid.java:75
int color
indexed color, see Terminal
Definition: Grid.java:84
double d
distance to source
Definition: Grid.java:86
Undirected graph that is defined implicitly by a regular 2d grid.
Definition: Grid.java:33
int get_column(int _index)
get columns from cell index
Definition: Grid.java:184
int num_rows()
get number of rows
Definition: Grid.java:167
void setup(String[] lines)
setup grid from string: create contents as grid cells
Definition: Grid.java:142
void setup(String text)
setup from multi-line string, e.g., EXAMPLE
Definition: Grid.java:162
int[] neighbors_from_tunnels(int index)
get neighbors of index from tunnels
Definition: Grid.java:233
void dfs_iterative(int s0)
iterative DFS
Definition: Grid.java:395
Grid(String filename)
construct grid by calling read_file
Definition: Grid.java:118
int get_row(int _index)
get row from cell index
Definition: Grid.java:180
int[] neighbors(int index)
get neighbors of cell index (4-neighborhood)
Definition: Grid.java:192
void display()
display current grid
Definition: Grid.java:317
int find_first(char value)
find first cell in grid with value @endiliteral
Definition: Grid.java:303
static final String EXAMPLE
example grid (argument to setup
Definition: Grid.java:44
int num_columns()
get number of columns
Definition: Grid.java:169
Cell cell(int i, int j)
get cell (i,j)
Definition: Grid.java:188
void reset_cells()
reset all cells to default state but keep their values
Definition: Grid.java:368
void backtrack(int s)
backtrack and display path to source
Definition: Grid.java:563
static void usage()
print help message and exit
Definition: Grid.java:577
int size()
get total number of cells
Definition: Grid.java:171
void dijkstra(int s0, int s1)
Dijkstra.
Definition: Grid.java:449
int[] create_tunnels_for(Cell cell)
create tunnels for specified entry at cell
Definition: Grid.java:252
static void main(String[] args)
Definition: Grid.java:594
int delay
delay in display in milliseconds
Definition: Grid.java:55
void bfs(int s0)
BFS.
Definition: Grid.java:422
Grid()
construct an empty grid, use read_file or setup
Definition: Grid.java:115
void dfs_recursive(int s)
recusive DFS
Definition: Grid.java:377
int cell_index(int i, int j)
get cell index from coordinates
Definition: Grid.java:174
void astar(int s0, int s1)
A*.
Definition: Grid.java:507
void read_file(String filename)
read file and treat contents as cells, calls setup
Definition: Grid.java:123
static final int GREEN
static final int BLACK
PrintStream out
output stream, user is responsible for flush
void cls(boolean clear)
clear screen (for clear=false, move only cursor)
void hideCursor()
hide cursor
void showCursor()
show cursor
static final int RED
void reset()
reset to black on white, normal font
static final int BLUE
static final int WHITE
static final int YELLOW
void fg(int color)
set foreground color
static final int CYAN
static final int MAGENTA
boolean dumb()
Is this a dumb terminal without colors?
static Terminal instance
singleton instance
void bg(int color)
set background color
utilities (not related to AuD lecture)
Definition: Colormap.java:1
AuD lecture: Data structures, algorithms, examples.
Definition: A234Tree.java:1