AuD
Lecture 'Algorithmen und Datenstrukturen' (code examples)
Grid2.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
12// Grid2.java is same as Grid.java but A^* handles tunnels correctly!
13// - Must preprocess: discover all tunnels.
14// - Fix heuristic (there are other/better options).
15
38public class Grid2 {
39
40 int num_rows_ = 0;
41 int num_columns_ = 0;
42 Cell[] cell_ = null;
43 int num_opened_ = 0;
44 boolean show_parent_ = false;
45 boolean ansi_term_ = true;
46 int frame_ = 0;
47
49 public final static String EXAMPLE =
50 "+-+ +----- --------+ +----+\n"+
51 "| | +------+----- ---+ | |\n"+
52 "| | A | | | |\n"+
53 "| | +------+----- ---+ | |\n"+
54 "+-+ | +----+\n"+
55 " +----- --------+\n"+
56 "\n"+
57 " +\n";
58
60 public int delay = 100;
61
76 public class Cell {
78 public char value = '\000';
80 public boolean is_free() {
81 return value=='\000' || value=='.' || value=='*' ||
82 Character.isWhitespace(value) ||
83 Character.isLetterOrDigit(value);
84 }
86 public boolean is_tunnel() { return 'a'<=value && value<='z'; }
87
89 public int color = Terminal.WHITE;
91 public double d = Double.POSITIVE_INFINITY;
93 public double f = Double.POSITIVE_INFINITY;
95 public int p = -1;
96 };
97
99 class CompareCellDistanceFromIndex implements Comparator<Integer> {
100 Grid2 grid;
101 CompareCellDistanceFromIndex(Grid2 g) { grid=g; }
102 @Override public int compare(Integer a,Integer b) {
103 Cell ca=grid.cell_[a.intValue()], cb=grid.cell_[b.intValue()];
104 double d=ca.d-cb.d ;
105 return (d<0.0) ? -1 : (d>0.0 ? +1 : 0);
106 }
107 };
109 class CompareCellEstimatedDistanceFromIndex implements Comparator<Integer> {
110 Grid2 grid;
111 CompareCellEstimatedDistanceFromIndex(Grid2 g) { grid=g; }
112 @Override public int compare(Integer a,Integer b) {
113 Cell ca=grid.cell_[a.intValue()], cb=grid.cell_[b.intValue()];
114 double d=ca.f-cb.f ;
115 return (d<0.0) ? -1 : (d>0.0 ? +1 : 0);
116 }
117 };
118
120 public Grid2() {}
121
123 public Grid2(String filename) {
124 read_file(filename);
125 }
126
128 public void read_file(String filename) {
129 StringBuilder sb=new StringBuilder();
130 try {
131 FileReader fr=new FileReader(filename);
132 BufferedReader in=new BufferedReader(fr);
133 String line;
134 while ((line=in.readLine())!= null) {
135 sb.append(line);
136 sb.append("\n");
137 }
138 } catch (Exception e) {
139 System.err.println("error while reading '"+filename+"'");
140 System.err.println(e.toString());
141 System.exit(-1);
142 }
143 setup(sb.toString());
144 }
145
147 public void setup(String[] lines) {
148 num_rows_=lines.length;
149 num_columns_=0; // find maximum number of columns
150 for (String line : lines) {
151 num_columns_=(num_columns_<line.length() ? line.length() : num_columns_);
152 }
153
154 cell_=new Cell[size()];
155 for (int i=0;i<size();++i)
156 cell_[i]=new Cell();
157
158 int i=0;
159 for (String line : lines) {
160 for (int j=0;j<line.length();++j) {
161 cell(i,j).value=line.charAt(j);
162 }
163 ++i;
164 }
165 }
167 public void setup(String text) {
168 setup(text.split("\\n"));
169 }
170
172 public int num_rows() { return num_rows_; }
174 public int num_columns() { return num_columns_; }
176 public int size() { return num_rows_*num_columns_; }
177
179 public int cell_index(int i,int j) {
180 assert(0<=i && i<num_rows_);
181 assert(0<=j && j<num_columns_);
182 return i*num_columns_+j;
183 }
185 public int get_row(int _index) {
186 return _index/num_columns_;
187 }
189 public int get_column(int _index) {
190 return _index%num_columns_;
191 }
193 public Cell cell(int i,int j) {
194 return cell_[cell_index(i,j)];
195 }
197 public int[] neighbors(int index) {
198 int i=get_row(index);
199 int j=get_column(index);
200 int north=-1,east=-1,south=-1,west=-1, k=0;
201
202 // if (i>0) { north=cell_index(i-1,j); ++k; }
203 // if (j<num_columns_-1) { east =cell_index(i,j+1); ++k; }
204 // if (i<num_rows_-1) { south=cell_index(i+1,j); ++k; }
205 // if (j>0) { west =cell_index(i,j-1); ++k; }
206
207 // equivalent but more efficient:
208 if (i>0) { north=index-num_columns_; ++k; }
209 if (j<num_columns_-1) { east =index+1; ++k; }
210 if (i<num_rows_-1) { south=index+num_columns_; ++k; }
211 if (j>0) { west =index-1; ++k; }
212
213 int[] nhd=new int[k];
214 k=0;
215
216 int[] tunnel_nhd = neighbors_from_tunnels(index);
217 if (tunnel_nhd.length > 1) {
218 // copy tunnels to nhd, but filter this cell index
219 nhd=new int[nhd.length + tunnel_nhd.length-1];
220 for (int n=0;n<tunnel_nhd.length;++n) {
221 if (tunnel_nhd[n] != index) {
222 nhd[k++] = tunnel_nhd[n];
223 }
224 }
225 }
226
227 if (north>=0) nhd[k++]=north;
228 if (east>=0) nhd[k++]=east;
229 if (south>=0) nhd[k++]=south;
230 if (west>=0) nhd[k++]=west;
231
232 return nhd;
233 }
234
235 private aud.HashMap<Character,int[]> tunnels_ = null;
236
238 protected int[] neighbors_from_tunnels(int index) {
239 Cell cell = cell_[index];
240
241 if (!cell.is_tunnel())
242 return new int[0];
243
244 if (tunnels_ == null)
245 tunnels_ = new aud.HashMap<Character,int[]>();
246
247 int[] nhd = tunnels_.find(cell.value);
248
249 if (nhd == null) {
251 }
252
253 return nhd;
254 }
255
257 protected int[] create_tunnels_for(Cell cell) {
258 assert cell.is_tunnel() : "expect tunnel entry";
259 // count ...
260 int n=0;
261 for (int i=0;i<cell_.length;++i) {
262 if (cell_[i].value == cell.value)
263 ++n;
264 }
265 int[] nhd = new int[n];
266
267 // ... save
268 tunnels_.insert(cell.value, nhd);
269
270 // .. and copy
271 n=0;
272 for (int i=0;i<cell_.length;++i) {
273 if (cell_[i].value == cell.value)
274 nhd[n++] = i;
275 }
276 return nhd;
277 }
278
280 protected boolean create_all_tunnels() {
281 boolean has_tunnels = false;
282 for (int i=0;i<cell_.length;i++) {
283 has_tunnels = has_tunnels || (neighbors_from_tunnels(i).length>0);
284 }
285 return has_tunnels;
286 }
287
305 double weight(int s,int t) {
306 char cs=cell_[s].value, ct=cell_[t].value;
307
308 double s_level=Character.isDigit(cs) ? Character.getNumericValue(cs) : 0.0;
309 double t_level=Character.isDigit(ct) ? Character.getNumericValue(ct) : 0.0;
310
311 return Math.abs(t_level-s_level)+1.0;
312 }
313
317 public int find_first(char value) {
318 for (int i=0;i<size();++i)
319 if (cell_[i].value==value)
320 return i;
321
322 Terminal.instance.reset();
323 System.err.println("Could not find a cell with value='"+value+"'.");
324 System.exit(-1);
325
326 return -1;
327 }
328
329
331 public void display() {
333
334 term.cls(frame_++==0);
335 term.fg(Terminal.BLACK);
336 term.hideCursor();
337
338 for (int i=0;i<num_rows();++i) {
339
340 for (int j=0;j<num_columns();++j) {
341 Cell c=cell(i,j);
342
343 term.bg(c.is_free() ? c.color : Terminal.BLUE);
344
345 char ch=c.value=='\000' ? ' ' : c.value;
346
347 // display characters for colors
348 if (term.dumb() && ch==' ') {
349 switch (c.color) {
350 case Terminal.RED: ch='&'; break;
351 case Terminal.BLUE: ch='@'; break;
352 case Terminal.GREEN: ch='$'; break;
353 case Terminal.YELLOW: ch='~'; break;
354 case Terminal.CYAN: ch='*'; break;
355 case Terminal.MAGENTA: ch='.'; break;
356 case Terminal.BLACK: ch='%'; break;
357 case Terminal.WHITE:
358 default: break;
359 }
360 }
361 else if (show_parent_) {
362 if (i>0 && c.p==cell_index(i-1,j)) ch='↓';
363 else if (i<num_rows_ && c.p==cell_index(i+1,j)) ch='↑';
364 else if (j>0 && c.p==cell_index(i,j-1)) ch='→';
365 else if (j<num_columns_ && c.p==cell_index(i,j+1)) ch='←';
366 }
367
368 term.out.print(ch);
369 }
370 term.out.println();
371 }
372 term.showCursor();
373 term.reset();
374
375 term.out.flush();
376 try {
377 Thread.sleep(delay);
378 } catch (InterruptedException e) {}
379 }
380
382 public void reset_cells() {
383 for (int i=0;i<size();++i) {
384 Cell c=cell_[i];
385 cell_[i]=new Cell();
386 cell_[i].value=c.value;
387 }
388 }
389
391 public void dfs_recursive(int s) {
392 cell_[s].color=Terminal.CYAN; // start processing s0
393 cell_[s].d=0.0;
394 display();
395 for (int t : neighbors(s)) {
396 if (cell_[t].color==Terminal.WHITE && cell_[t].is_free()) {
397
398 cell_[t].d=cell_[s].d+weight(s,t);
399 cell_[t].p=s;
400
401 dfs_recursive(t);
402 }
403 }
404 cell_[s].color=Terminal.MAGENTA; // done with s0
405 display();
406 }
407
409 public void dfs_iterative(int s0) {
411
412 open.push(s0);
413 cell_[s0].color=Terminal.CYAN; // start processing
414 cell_[s0].d=0.0;
415 display();
416
417 while (!open.is_empty()) {
418 int s=open.pop();
419
420 for (int t : neighbors(s)) {
421 if (cell_[t].color==Terminal.WHITE && cell_[t].is_free()) {
422
423 cell_[t].color=Terminal.CYAN; // start processing
424 cell_[t].d=cell_[s].d+weight(s,t);
425 cell_[t].p=s;
426
427 open.push(t);
428 }
429 }
430 cell_[s].color=Terminal.MAGENTA;
431 display();
432 }
433 }
434
436 public void bfs(int s0) {
438
439 open.enqueue(s0);
440 cell_[s0].color=Terminal.CYAN; // start processing
441 cell_[s0].d=0.0;
442 display();
443
444 while (!open.is_empty()) {
445 int s=open.dequeue();
446
447 for (int t : neighbors(s)) {
448 if (cell_[t].color==Terminal.WHITE && cell_[t].is_free()) {
449
450 cell_[t].color=Terminal.CYAN; // start processing
451 cell_[t].d=cell_[s].d+weight(s,t);
452 cell_[t].p=s;
453
454 open.enqueue(t);
455 }
456 }
457 cell_[s].color=Terminal.MAGENTA;
458 display();
459 }
460 }
461
463 public void dijkstra(int s0,int s1) {
464
466 new PriorityQueue<Integer>(size(),new CompareCellDistanceFromIndex(this));
467
468 open.push(s0);
469 ++num_opened_;
470 cell_[s0].color=Terminal.CYAN; // start processing
471 cell_[s0].d=0.0;
472 display();
473
474 while (!open.is_empty()) {
475 int s=open.pop();
476
477 for (int t : neighbors(s))
478 if (cell_[t].is_free()) {
479
480 double priority=cell_[s].d+weight(s,t);
481
482 if (Double.isInfinite(cell_[t].d)) {
483
484 assert(!open.contains(t));
485
486 cell_[t].color=Terminal.CYAN; // start processing
487 cell_[t].p=s;
488 cell_[t].d=priority;
489
490 open.push(t);
491 ++num_opened_;
492 }
493 else if (cell_[t].d>priority && open.contains(t)) {
494 cell_[t].color=Terminal.YELLOW; // got updated
495 cell_[t].p=s;
496 cell_[t].d=priority;
497
498 open.lower(t); // adapt priority queue to new, lower priority
499 }
500 }
501
502 cell_[s].color=Terminal.MAGENTA;
503 display();
504
505 if (s==s1) {
506 backtrack(s);
507 break;
508 }
509 }
510 }
511
513 double estimate_distance(int s,int t) {
514 int dx=get_column(s)-get_column(t);
515 int dy=get_row(s)-get_row(t);
516
517 //return Math.abs((double) dx)+Math.abs((double) dy);
518 double d=Math.abs((double) dx)+Math.abs((double) dy);
519
520 if (tunnels_ != null) {
521 System.err.println(tunnels_.size());
522 for (aud.HashMap<Character,int[]>.Entry tunnel: tunnels_.entries()) {
523 for (int index : tunnel.value) {
524 int dtx=get_column(s)-get_column(index);
525 int dty=get_row(s)-get_row(index);
526
527 double dt=Math.abs((double) dtx)+Math.abs((double) dty);
528 if (dt<d)
529 d=dt;
530 }
531 }
532 }
533
534 return d;
535 }
536
538 public void astar(int s0,int s1) {
539
540 create_all_tunnels(); // May fail if there are any tunnels, and they were not initialized!
541
544 (size(),new CompareCellEstimatedDistanceFromIndex(this));
545
546 open.push(s0);
547 ++num_opened_;
548 cell_[s0].color=Terminal.CYAN; // start processing
549 cell_[s0].d=0.0;
550 cell_[s0].f=estimate_distance(s0,s1); // initial guess
551 display();
552
553 while (!open.is_empty()) {
554 int s=open.pop();
555
556 for (int t : neighbors(s))
557 if (cell_[t].is_free()) {
558
559 double distance=cell_[s].d+weight(s,t);
560
561 double priority = distance + estimate_distance(t,s1);
562
563 if (Double.isInfinite(cell_[t].d)) {
564
565 assert(!open.contains(t));
566
567 cell_[t].color=Terminal.CYAN; // start processing
568 cell_[t].p=s;
569 cell_[t].d=distance;
570 cell_[t].f=priority;
571
572 open.push(t);
573 ++num_opened_;
574 }
575 else if (cell_[t].d>distance && open.contains(t)) {
576 cell_[t].color=Terminal.YELLOW; // got updated
577 cell_[t].p=s;
578 cell_[t].d=distance;
579 cell_[t].f=priority;
580
581 open.lower(t); // adapt priority queue to new, lower priority
582 }
583 }
584
585 cell_[s].color=Terminal.MAGENTA;
586 display();
587
588 if (s==s1) {
589 backtrack(s);
590 break;
591 }
592 }
593 }
594
596 public void backtrack(int s) {
597 assert(cell_[s].p>=0);
598 double d=cell_[s].d;
599 while (s>=0) {
600 cell_[s].color=Terminal.GREEN;
601 s=cell_[s].p;
602 display();
603 }
604 Terminal.instance.out.println("distance = "+d);
605 if (num_opened_>0)
606 Terminal.instance.out.println("examined "+num_opened_+" cells in total");
607 }
608
610 protected static void usage() {
611 System.err.println
612 ("java aud.example.graph.TraversalExample "+
613 "[-g file] [-m method] [-s s0] [-d s1] [-v N]\n"+
614 " -g FILE read grid from FILE\n"+
615 " -m METHOD traversal {dfs,idfs,bfs,dijkstra,astar}\n"+
616 " -p show parent cell by an arrow (requires color)\n"+
617 " -s START start node (one character, default: 'A')\n"+
618 " -d DESTINATION destination node for A* (default: none)\n"+
619 " -delay MILLISECONDS set delay after each step\n"+
620 " -no-color don't use ANSI codes\n"+
621 " -color force use of ANSI codes (requires terminal emulation)\n"+
622 " -show display initial grid and exit"
623 );
624 System.exit(-1);
625 }
626
627 public static void main(String[] args) {
628
629 Runtime.getRuntime().addShutdownHook(new Thread() {
630 @Override public void run() {
631 // make sure we are leaving cleanly on SIGINT
632 Terminal.instance.reset();
633 Terminal.instance.showCursor();
634 Terminal.instance.out.flush();
635 }
636 });
637
638
639 Grid2 g=new Grid2();
640
641 // prcess argumentys
642 String method="dfs";
643 char start='A', destination='A';
644
645 for (int i=0;i<args.length;++i) {
646 if (args[i].compareTo("-g")==0) {
647 g.read_file(args[++i]);
648 if (g.size()==0) {
649 System.err.println("error: grid is empty");
650 System.exit(-1);
651 }
652 }
653 else if (args[i].compareTo("-m")==0)
654 method=args[++i];
655 else if (args[i].compareTo("-s")==0)
656 start=args[++i].charAt(0);
657 else if (args[i].compareTo("-d")==0)
658 destination=args[++i].charAt(0);
659 else if (args[i].compareTo("-delay")==0)
660 g.delay=Integer.parseInt(args[++i]);
661 else if (args[i].compareTo("-p")==0)
662 g.show_parent_=true;
663 else if (args[i].compareTo("-no-color")==0)
664 Terminal.instance=new Terminal(false);
665 else if (args[i].compareTo("-color")==0)
666 Terminal.instance=new Terminal(true);
667 else if (args[i].compareTo("-show")==0)
668 method=null;
669 else
670 usage();
671 }
672
673 if (g.size()==0)
674 g.setup(EXAMPLE); // use default grid
675
676 g.display();
677
678 if (method==null) // show only
679 System.exit(0);
680
681 // find source node (or exit)
682 int s0=g.find_first(start);
683
684 // find destination node if given (or exit)
685 int s1=(destination!=start ? g.find_first(destination) : -1);
686
687 if (method.compareTo("dfs")==0)
688 g.dfs_recursive(s0);
689 else if (method.compareTo("idfs")==0)
690 g.dfs_iterative(s0);
691 else if (method.compareTo("bfs")==0)
692 g.bfs(s0);
693 else if (method.compareTo("dijkstra")==0)
694 g.dijkstra(s0,s1);
695 else if (method.compareTo("astar")==0) {
696 if (s1<0) {
697 System.err.println("A* requires destination, use '-d'");
698 System.exit(-1);
699 }
700 g.astar(s0,s1);
701 }
702 else
703 usage();
704
705 }
706}
entry (key,value) in HashMap (iterator)
Definition: HashMap.java:65
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 Grid2.
Definition: Grid2.java:76
int color
indexed color, see Terminal
Definition: Grid2.java:89
boolean is_free()
Can we enter this cell?
Definition: Grid2.java:80
char value
the value that is displayed
Definition: Grid2.java:78
boolean is_tunnel()
letters a-z denote entries to tunnels: e.g., all 'a' are connected
Definition: Grid2.java:86
double f
priority used by A* (distance + estimated distance to destination
Definition: Grid2.java:93
int p
index of parent cell
Definition: Grid2.java:95
double d
distance to source
Definition: Grid2.java:91
Undirected graph that is defined implicitly by a regular 2d grid.
Definition: Grid2.java:38
int size()
get total number of cells
Definition: Grid2.java:176
void read_file(String filename)
read file and treat contents as cells, calls setup
Definition: Grid2.java:128
void setup(String[] lines)
setup grid from string: create contents as grid cells
Definition: Grid2.java:147
boolean create_all_tunnels()
create all tunnels (preprocess)
Definition: Grid2.java:280
int delay
delay in display in milliseconds
Definition: Grid2.java:60
int[] neighbors_from_tunnels(int index)
get neighbors of index from tunnels
Definition: Grid2.java:238
Grid2(String filename)
construct grid by calling read_file
Definition: Grid2.java:123
int get_row(int _index)
get row from cell index
Definition: Grid2.java:185
int cell_index(int i, int j)
get cell index from coordinates
Definition: Grid2.java:179
int find_first(char value)
find first cell in grid with value @endiliteral
Definition: Grid2.java:317
void display()
display current grid
Definition: Grid2.java:331
static final String EXAMPLE
example grid (argument to setup
Definition: Grid2.java:49
void setup(String text)
setup from multi-line string, e.g., EXAMPLE
Definition: Grid2.java:167
void astar(int s0, int s1)
A*.
Definition: Grid2.java:538
void backtrack(int s)
backtrack and display path to source
Definition: Grid2.java:596
void bfs(int s0)
BFS.
Definition: Grid2.java:436
int get_column(int _index)
get columns from cell index
Definition: Grid2.java:189
void dijkstra(int s0, int s1)
Dijkstra.
Definition: Grid2.java:463
int[] neighbors(int index)
get neighbors of cell index (4-neighborhood)
Definition: Grid2.java:197
static void main(String[] args)
Definition: Grid2.java:627
int[] create_tunnels_for(Cell cell)
create tunnels for specified entry at cell
Definition: Grid2.java:257
void reset_cells()
reset all cells to default state but keep their values
Definition: Grid2.java:382
Cell cell(int i, int j)
get cell (i,j)
Definition: Grid2.java:193
int num_rows()
get number of rows
Definition: Grid2.java:172
void dfs_iterative(int s0)
iterative DFS
Definition: Grid2.java:409
Grid2()
construct an empty grid, use read_file or setup
Definition: Grid2.java:120
int num_columns()
get number of columns
Definition: Grid2.java:174
static void usage()
print help message and exit
Definition: Grid2.java:610
void dfs_recursive(int s)
recusive DFS
Definition: Grid2.java:391
double f
priority used by A* (distance + estimated distance to destination
Definition: Grid.java:88
double d
distance to source
Definition: Grid.java:86
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