import java.security.InvalidParameterException; import java.util.*; import java.io.File; import java.io.FileNotFoundException; /** * @author Eric Durant * Created on November 7, 2017 * Last updated November 16, 2017 * Java sample solutions for Op 2017 */ public class Op2017 { /** * Console menu allowing selection of any of the 9 demo solutions * @param args (unused) */ public static void main(String[] args) { boolean done = false; while(!done) { System.out.println("Op 2017 Demo Program"); System.out.println("1. Popular letter"); System.out.println("2. Pythagorean triple"); System.out.println("3. Crossword assembly"); System.out.println("4. Finding pi"); System.out.println("5. Pizza schedule"); System.out.println("6. Dedekind psi function"); System.out.println("7. Internal triangle area"); System.out.println("8. Life simulation"); System.out.println("9. Commuter dilemma"); System.out.println("0. Exit"); int choice = -1; // invalid while (choice > 9 || choice < 0) { if (sc.hasNextInt()) { choice = sc.nextInt(); sc.nextLine(); // dump CR from stream } else sc.next(); // discard non-int token } try { switch(choice) { case 1: prob1(); break; case 2: prob2(); break; case 3: prob3(); break; case 4: prob4(); break; case 5: prob5(); break; case 6: prob6(); break; case 7: prob7(); break; case 8: prob8(); break; case 9: prob9(); break; case 0: done = true; break; } } catch (FileNotFoundException e) { System.err.println("Op Demo returned to main menu due to FileNotFoundException: " + e.getMessage()); } catch (RuntimeException e) { System.err.println("Op Demo returned to main menu due to RuntimeException: " + e.getMessage()); } } } /** * Problem 1. Popular letter (10 Points) */ private static void prob1() { /* * Ask the user to enter two unique, individual letters in lowercase followed by a word in lowercase. Then output the letter that occurs more often in the word. If the letters occur equally often (including 0 times), output both of them. */ final char[] c = new char[] { inputChar("First lowercase character: "), inputChar("Second lowercase character: ") }; final String word = inputLine("Lowercase word? "); final int[] ct = new int[c.length]; for (char l : word.toCharArray()) { for (int i = 0; i < ct.length; ++i) if (l == c[i]) ++ct[i]; } if (ct[0] >= ct[1]) System.out.println(c[0]); if (ct[1] >= ct[0]) System.out.println(c[1]); } /** * Problem 2. Pythagorean triple (10 Points) */ private static void prob2() { /* * A Pythagorean triple is a set of positive integers such that a2+b2=c2. c represents the length of the hypotenuse of a right triangle, while a and b are the lengths of the other legs. Prompt the user for a, b, and c (you may assume they enter the largest value in c), and then tell them whether those numbers form a Pythagorean triple. */ final int a = inputUnsignedInt("a? "), b = inputUnsignedInt("b? "), c = inputUnsignedInt("c? "); System.out.printf("The inputs %sform a Pythagorean triple.\n", (a*a + b*b == c*c)?"":"do not "); } /** * Problem 3. Crossword assembly (10 Points) */ private static void prob3() { /* * Prompt the user for two words in lowercase and then describe all the ways they can cross in a crossword puzzle. They can cross wherever they have the same letter in a position. Start counting letter positions at 1. For example, • Input: dog, bot; output: (2,2) • Input: cat, dog; output: [blank may be used to indicate no crossings] • Input: crisis, oasis: (3,4), (4,3), (4,5), (5,4), (6,3), (6,5) */ final String[] words = new String[] { inputLine("Lowercase word 1? "), inputLine("Lowercase word 2? ")}; for (int i = 0; i < words[0].length(); ++i) for (int j = 0; j < words[1].length(); ++j) if (words[0].charAt(i)==words[1].charAt(j)) System.out.printf("(%d,%d), ", i+1, j+1); // extra comma at end is okay System.out.println(); } /** * Problem 4. Finding pi (20 Points) */ private static void prob4() { /* * Adding the reciprocals of all the square numbers converges to π2/6. Prompt the user for a maximum number and then iterate through all the partial sums starting with the reciprocal of the square of 1, then adding the reciprocal of the square of 2, and so on through the reciprocal of the square of the user-specified maximum. At each iteration, estimate π by multiplying the sum by 6 and then taking the square root; output each of these estimates on a separate line. If you do this correctly, the output will converge towards π. */ final int max = inputUnsignedInt("Maximum? "); double sum = 0; for (int i = 1; i <= max; ++i) { sum += 1 / (double)(i*i); System.out.printf("%g\n", Math.sqrt(6 * sum)); } } /** * Problem 5. Pizza schedule (20 Points) */ private static void prob5() { /* * This problem is inspired by a problem by MSOE’s Dr. Gary Au. A family has children who all love pizza, so they get pizza for a meal once per day. Unfortunately, all the children have a different favorite, but, due to pizza pricing ($20 for the “family pleaser” and $18 for the “tiny”), their parents order one “family pleaser” sized pizza each day. However, the children have been taught to compromise, so each child is content to eat their non-favorite pizza as long as they get their favorite with a certain regularity. Your program will begin by prompting for each child’s “tolerance period,” that is how many days without their favorite pizza it takes for them to become discontented. For example, if the number of days is 2, the child must have their favorite pizza at least every other day, but a pattern like Monday, Tuesday, Thursday, Friday, Sunday, … would also work. No two children have the same tolerance period and each tolerance period is at least 2. It is unknown how many children there are, so the user will enter a 0 to indicate that the last child’s tolerance period has been entered. With this information in hand, you will attempt to put together a 365-day (days 0 through 364) pizza schedule for the family using a greedy approach. Specifically, you will first take the least tolerant child and schedule their favorite pizza on as few days as possible and starting as late as possible. For example, if the least tolerant child will become mad on the 3rd day without their pizza, you would schedule days 2, 5, 8, 11, 14, …, 359, 362. Then, you would proceed to schedule the second least tolerant child, again starting on the last possible day and scheduling their pizza as seldom as possible. If, for example, the next child would become mad on the 5th day, you would schedule their pizza on… • Day 4 (if there were no pizza on days 0 through 4, that would be 5 days without their pizza) • Day 9 (5 days later) • Day 13 (you’d like to go to 9+5 = 14, but pizza 3 is already scheduled then, so you need to back off a day) • … It is possible that if the family is demanding enough you will determine that no schedule is possible using the greedy approach described above. If so, report that to the user. Otherwise, output the pizza schedule, which is a list of 365 numbers with each number representing a specific child by their “tolerance period”. Use a 0 to represent any days on which it is not necessary to buy a pizza to keep any child happy. */ final int N = 365; // days in a year ArrayList p = new ArrayList<>(); int nextP; do { nextP = inputUnsignedInt("Next period? "); if (nextP == 0) break; p.add(nextP); } while (true); // Greedy scheduler, start with highest global priority and then fill in // with backtracking approach until and unless it fails (non-schedulable). Collections.sort(p); // ensure priority order, lowest period / highest priority first int[] S = new int[N]; // a 0 in the schedule indicates nothing to do. Scheduling periods will be // used to tag tasks (not a problem until there can be 2 jobs with the same period). // latest possible time for highest priority. There are no conflicts on the first scheduled task. for (int idx = p.get(0)-1; idx < N; idx += p.get(0)) S[idx] = p.get(0); boolean success = true; // a global schedule was found for (int idx = 1; idx < p.size(); ++idx) { int left = 0; // left scheduling bound int right = p.get(idx)-1; while (right >= left) { if (S[right] == 0) { S[right] = p.get(idx); // schedule it left = right + 1; right = left + p.get(idx) - 1; if (right >= N) // element satisfied through end of schedule break; } else { right = right - 1; if (right < left) { System.err.println("Cannot schedule"); success = false; } } } } if(success) { System.out.println("The schedule is..."); dumpIntVector(S); } } /** * Problem 6. Dedekind psi function (20 Points) */ private static void prob6() { /* * The Dedekind ψ (psi) function is defined for positive integers n to have the value which is the product of n and all terms (1+1/p) where p is a distinct prime factor of n. Distinct means that a prime factor is only used once even if it appears multiple times in the prime factorization of n. Remember that 1 has no prime factors since the first prime is 2. Here are how some values of the function are calculated: n ψ(n) 1 1 2 2(1+1/2) = 2(3/2) = 3 3 3(1+1/3) = 3(4/3) = 4 4 4(1+1/2) = 4(3/2) = 6 6 6(1+1/2)(1+1/3) = 6(3/2)(4/3) = 12 30 30(1+1/2)(1+1/3)(1+1/5) = 30(3/2)(4/3)(6/5) = 72 Note that it is not necessary to use decimals to perform these calculations. The result will always be an integer. Your program will prompt the user for n and then calculate and output ψ(n). Do this using only integer data types (using floating point data types can introduce unwanted round off error.) Your program must work correctly for n from 1 to 10,000. */ final int n = inputUnsignedInt("n? "); final List factors = factor(n); final List uniqueFactors = new ArrayList<>(new HashSet<>(factors)); int r = n; for (int f : uniqueFactors) { r /= f; r *= f+1; } System.out.printf("psi(%d) is %d.\n", n, r); } public static boolean isPrime(int n) { final int upper = (int)Math.sqrt(n); // truncation is okay for (int d = 2; d <= upper; ++d) if (n % d == 0) return false; return true; } public static int nextPrime(int n) { int rv = n+1; while (!isPrime(rv)) ++rv; return rv; } public static List factor(int n) { List li = new ArrayList<>(); int lower = 2; // current prime while (n > 1) { if (n % lower == 0) { li.add(lower); n /= lower; } else { lower = nextPrime(lower); } } return li; } /** * Problem 7. Internal triangle area (40 Points) */ private static void prob7() { /* * Prompt the user to enter (x,y) coordinates for three vertices of a triangle, A, B, and C. At each step, it should be clear what you’re prompting for (e.g., “A’s x coordinate” or “Ax”). Then have the user enter a proportion, t, between 0 and 0.5. This proportion is used to construct a triangle inside the original triangle as shown in the figure. In particular, the line q starts at B and ends at a point p of the distance from C to A. Similarly, the line r starts at C and ends at a point t of the distance from A to B. And, the line p starts at A and ends at a point p of the distance from B to C. The internal triangle, shaded in the figure, is formed by these 3 internal lines. Using any method you chose, calculate the following values. Remember that you are allowed to consult online references about formulas for areas of triangles, etc. • The area of the original triangle entered by the user • The area of the internal triangle • The ratio of the 2nd area to the 1st one. This ratio cannot be greater than 1. Image source: https://en.wikipedia.org/wiki/One-seventh_area_triangle#/media/File:One-seventh_area_triangle.svg */ // This solution is vulnerable to division by 0 when the given or constructed triangle has vertical edges. // Solutions for the competition are allowed to have this limitation. final boolean DEBUG = false; // include extra debugging output final int V = 3, C = 2; // vertex, spatial dimension constants final double[][] triangle = new double[V][C]; for (int v = 0; v < triangle.length; ++v) for (int xy = 0; xy < triangle[0].length; ++xy) triangle[v][xy] = inputDouble(String.format("%c%c? ", 'A'+v, 'x'+xy)); final double p = inputDouble("Proportion (0-0.5)? "); // can assume valid per initial instructions // find the points p along the opposite sides final double[][] pPoint = new double[V][C]; for (int v = 0; v < triangle.length; ++v) for (int xy = 0; xy < triangle[0].length; ++xy) pPoint[v][xy] = (1-p) * triangle[(v+1)%V][xy] + p * triangle[(v+2)%V][xy]; if (DEBUG) { System.out.print("DEBUG: Anchor points along outer edges: "); dumpTupleArray(pPoint); System.out.println(); } // find slope & intercept of internal lines final double[] m = new double[V]; final double[] b = new double[V]; for (int v = 0; v < triangle.length; ++v) { m[v] = (triangle[v][1]-pPoint[v][1]) / (triangle[v][0]-pPoint[v][0]); b[v] = triangle[v][1] - m[v] * triangle[v][0]; } if (DEBUG) { System.out.println("DEBUG: sides of internal triangle..."); for (int v = 0; v < triangle.length; ++v) { System.out.printf("y = %g x + %g\n", m[v], b[v]); } } // now find intersections of the line pairs final double[][] triangle2 = new double[triangle.length][triangle[0].length]; for (int v = 0; v < triangle.length; ++v) { triangle2[v][0] = (b[(v+2)%V] - b[(v+1)%V]) / (m[(v+1)%V] - m[(v+2)%V]); // x = (b2-b1)/(m1-m2); triangle2[v][1] = m[(v+1)%V] * triangle2[v][0] + b[(v+1)%V]; // y = m1x+b1; } if (DEBUG) { System.out.print("DEBUG: Internal triangle vertices: "); dumpTupleArray(triangle2); System.out.println(); } // Areas from vertices: https://www.mathopenref.com/coordtrianglearea.html double areaOuter = 0d, areaInner = 0d; for (int v = 0; v < triangle.length; ++v) { areaOuter += triangle [v][0]*(triangle [(v+1)%V][1]-triangle [(v+2)%V][1]); areaInner += triangle2[v][0]*(triangle2[(v+1)%V][1]-triangle2[(v+2)%V][1]); } areaOuter = Math.abs(areaOuter) / 2; areaInner = Math.abs(areaInner) / 2; System.out.printf("Original triangle area = %g\nInternal triangle area = %g, Ratio 2nd to 1st area = %g\n", areaOuter, areaInner, areaInner/areaOuter); } /** * Problem 8. Life simulation (40 Points) */ private static void prob8() throws FileNotFoundException { /* * Write a program that executes Conway’s Game of Life. Begin by prompting the user for a filename. The filename will then be used to read in a 2-D array of values representing an initial board position. The first line of the file contains two integers separated by whitespace; these integers represent the number of rows and the number of columns, respectively. The subsequent lines use 0s and 1s to indicate which elements of the array contain (1) or do not contain (0) a live cell: For example: 6 6 000000 000000 001110 011100 000000 000000 You will then output the pattern to the console, using * to indicate a live cell and a blank to indicate a dead cell. For example, the output corresponding to the above file would be as shown at right, where light dots are added for clarity to indicate spaces. After outputting the pattern, you will output “==========” (10 equals signs) on a line and then calculate what happens to the array of cells in the next generation: • If a cell is alive but only has 0 or 1 live neighbors, it dies (underpopulation). • If a cell is alive and has 2 or 3 live neighbors, it survives. • If a cell is alive but has more than 3 live neighbors, it dies (overpopulation). • If a cell is dead but has exactly 3 live neighbors, is becomes alive (reproduction). You will then output the new generation. For the above example, it would be as shown at right. Your program will then calculate and output the next generation. This will continue forever (until the user uses means external to your code to terminate the program). Neighbors include diagonal cells, so there are generally 8 neighbors, but cells at the edges only have 5 neighbors while cells in the corners only have 3 neighbors. Be careful that each cell’s status in the new generation is determined by the status of the cells surrounding it in the previous generation (not a mix of the previous and current generation). */ final String filename = inputLine("File to read? "); boolean[][] world; try { world = readBooleanArray(new Scanner(new File(filename))); } catch (FileNotFoundException e) { System.err.println("prob8 could not find its input file, rethrowing exception..."); throw e; } catch (Exception e) { System.err.println("prob8 could not parse its input file, rethrowing..."); throw e; } final int rows = world.length; final int cols = world[0].length; boolean[][] world2 = new boolean[rows][cols]; // algorithm suggests double buffer do { dumpBooleanArray(world, new char[] {' ', '*'}); final String delimit = "=========="; try { Thread.sleep(1000); } catch (InterruptedException e) { } System.out.println(delimit); // calculate the convolution with the 3x3 "donut" kernel to get the number of live neighbors // Having 1 extra cell all the way around eliminates all the edge cases and regularizes matrix op int[][] sa = new int[rows+2][cols+2]; // summing array for (int ro = -1; ro <= 1; ++ro) // 3x3 convolution offsets for (int co = -1; co <= 1; ++co) if (ro != 0 || co != 0) // central point isn't in kernel for (int r = 0; r < rows; ++r) // world matrix iteration for (int c = 0; c < cols; ++c) sa[r+ro+1][c+co+1] += world[r][c] ? 1 : 0; for (int r = 0; r < rows; ++r) // world matrix iteration for (int c = 0; c < cols; ++c) { if (world[r][c]) // alive world2[r][c] = sa[r+1][c+1] == 2 || sa[r+1][c+1] == 3; // survive, else die from over-/underpopulation else // dead world2[r][c] = sa[r+1][c+1] == 3; // reproduction, else remain dead } // swap worlds, avoid realloc boolean[][] temp = world2; world2 = world; world = temp; } while (true); } /** * Problem 9. Commuter dilemma (40 Points) */ private static void prob9() { /* * This problem is inspired by a problem published in the Wall Street Journal on October 12, 2017. That problem is also the source of the graphic. (https://blogs.wsj.com/puzzle/2017/10/12/varsity-math-week-109/) During every morning rush hour, M cars need to get from H to S, where travel times between points are shown on the map at right. Each driver needs to make 2 decisions: first, whether to head to intermediate point A or B, and, second, whether to take the AB bridge before proceeding to S. You will write a simulation tracking which cars take which route over a D-day period. On day 0, each driver selects a route randomly (both alternatives equally probable at 50%); you will need to remember this as the driver’s preferred route, which may change during the simulation. The commute times from H to A (HA) and from B to S (BS) vary depending on how many cars are travelling that particular leg. On each day of the simulation you will output a row of a table indicating: • Day number: 0 through D-1 • HA: number of cars going from H to A • AB: number of cars going from A to B • BA: number of cars going from B to A • BS: number of cars going from B to S • Average time: the average time it takes a car to get from H to S. • Minimum time: the shortest time to complete the HS route among the 4 alternatives. Even if 0 cars take a particular path, it is still a valid alternative for this calculation. • Maximum time: analogous to “minimum time”, but by taking the longest among the alternatives After each day, each driver learns of the minimum time for the day. Some of the drivers (proportion p, 0 < p ≤ 1, input by the user) who did not make the commute in the minimum time change their routes. Specifically, a rerouting driver makes exactly one change; if changing their initial leg will save them at least 5 minutes (using the leg commute times of the day just finished) they do it; otherwise they change their decision about taking the bridge. The other drivers stay on their old route for at least another day. At the beginning of your simulation, you will prompt the user for • M, the number of cars, • D, the number of days, and • p, the probability of rerouting when suboptimal and then proceed through your simulation, outputting each row as it is calculated. Be sure to test your program with various numbers of cars, including 0, 1, 10, 100, 1000, 10,000, and 100,000. */ final int t_hb = 100, t_as = 50, t_ab_ba = 10; final int M = inputUnsignedInt("Number of cars? "); final int D = inputUnsignedInt("Number of days? "); final float p = inputFloat("Suboptimal reroute probability? "); final boolean itin[][] = new boolean[M][2]; // cols are 2 choices: false = A & false = don't take bridge final java.util.Random myGenerator = new java.util.Random(); // random initialization for (int r = 0; r < itin.length; ++r) for (int c = 0; c < itin[0].length; ++c) itin[r][c] = myGenerator.nextBoolean(); System.out.println("Day\t\tHA\t\tAB\t\tBA\t\tBS\tAve.\tMin.\tMax.\n"); // header for (int day = 0; day < D; ++day) { // iterate to find cars on each path int ha = 0, ab = 0, ba = 0, bs = 0; // naming convention is from_to for (int r = 0; r < itin.length; ++r) { if (!itin[r][0]) ++ha; if (itin[r][1]) { // took bridge if (itin[r][0]) ++ba; else ++ab; } if (itin[r][0] ^ itin[r][1]) ++bs; // XOR: B and not bridge, or A(=~B) and bridge } float t_ha = ha / (float)200, t_bs = bs / (float)100; // iterate to find average time float[] carTime = new float[M]; float totalTime = 0; for (int r = 0; r < itin.length; ++r) { carTime[r] = (itin[r][0] ? t_hb : t_ha) + (itin[r][1] ? t_ab_ba : 0) + (itin[r][0] ^ itin[r][1] ? t_bs : t_as); totalTime += carTime[r]; } final float averageTime = totalTime / M; // minimum & maximum time, calculate 4 paths and sort final float[] routeTimes = {t_ha + t_as, t_hb + t_bs, // 00 01 t_ha + t_ab_ba + t_bs, t_hb + t_ab_ba + t_as}; // 10 11 // determine index of quickest path int shortestPathId = 0; for (int trial = 1; trial < routeTimes.length; ++trial) if (routeTimes[trial] < routeTimes[shortestPathId]) shortestPathId = trial; //System.out.print("DEBUG shortestPathId=" + shortestPathId + ' '); Arrays.sort(routeTimes); // index no longer corresponds to time, but that's okay; we use them separately... System.out.printf("%2d\t%6d\t%6d\t%6d\t%6d\t%6.1f\t%6.1f\t%6.1f\n", day, ha, ab, ba, bs, averageTime, routeTimes[0], routeTimes[routeTimes.length-1]); // cars not arriving in minimum time randomly change their routes per problem statement int DEBUG_suboptimal = 0, DEBUG_changing = 0; boolean[] optimalRoute = {(shortestPathId & 1) != 0, (shortestPathId & 2) != 0}; // this looks weird since LSB is on left in array initializer //System.out.print("DEBUG Optimal = " + (optimalRoute[0]?'B':'A') + " " + (optimalRoute[1]?"":"no") + " bridge "); for (int r = 0; r < itin.length; ++r) { if (carTime[r] > routeTimes[0] + 0.01) { // ?? handle round-off? ++DEBUG_suboptimal; if (myGenerator.nextFloat() < p) { ++DEBUG_changing; for (int c = 0; c < itin[0].length; ++c) itin[r][c] = optimalRoute[c]; // Fully random rerouting (original problem statement) results in instability! // The instability results when the 2 shortest routes are near the same duration // (as happens near convergence) and // thus near-optimal drivers doing a full-random rerouting greatly increase their time. //for (int c = 0; c < itin[0].length; ++c) // itin[r][c] = myGenerator.nextBoolean(); } } } if (DEBUG_suboptimal == 0) break; // converged; nothing can change; student solutions DON'T need to end early like this //System.out.print("DEBUG: reroutes = " + DEBUG_changing + '/' + DEBUG_suboptimal + ' '); } } /** * Dump boolean array to System.out * @param b array to dump * @param syms 2-char array containing false and true symbol, respectively */ private static void dumpBooleanArray(boolean[][] b, char[] syms) { if (syms.length != 2) throw new InvalidParameterException("syms must be exactly 2 characters (representing false and true"); for(int r = 0; r < b.length; ++r) { for(int c = 0; c < b[r].length; ++c) { System.out.print(b[r][c]?syms[1]:syms[0]); System.out.print(' '); } System.out.println(); } } private static void dumpBooleanArray(boolean[][] b) { dumpBooleanArray(b, new char[] {'0', '1'}); } private static void dumpIntArray(int[][] b) { for(int r = 0; r < b.length; ++r) dumpIntVector(b[r]); } private static void dumpIntVector(int[] v) { for(int c = 0; c < v.length; ++c) { System.out.print(v[c]); System.out.print(' '); } System.out.println(); } private static void dumpFloatVector(float [] v) { for(int c = 0; c < v.length; ++c) { System.out.print(v[c]); System.out.print(' '); } System.out.println(); } private static void dumpTupleArray(double[][] a) { for (int v = 0; v < a.length; ++v) { if (v != 0) System.out.print(", "); // prefix next tuple System.out.print('('); // begin tuple for (int i = 0; i < a[0].length; ++i) { if (i != 0) System.out.print(','); // prefix next coordinate System.out.print(a[v][i]); } System.out.print(')'); // end tuple } } /**Read a NxN array. * Assumes properly formatted input. * @param sc the Scanner to read from * @param N side length of array * @return the NxN array */ private static int[][] readIntArray(Scanner sc, int N) { int[][] result = null; assert N >= 0; if (sc != null) { result = new int[N][N]; boolean success = true; for(int row = 0; row < N && success; ++row) { for(int col = 0; col < N; ++col) { if (sc.hasNextInt()) { result[row][col] = sc.nextInt(); } else { success = false; break; } } } if(!success) { throw new RuntimeException("Fewer than the expected number of elements found.\n"); } //sc.close(); } return result; } /**Read a NxN array. * Assumes properly formatted input. * @param sc the Scanner to read from * @param N side length of array * @return the NxN array */ private static double[][] readDoubleArray(Scanner sc, int N) { double[][] result = null; assert N >= 0; if (sc != null) { result = new double[N][N]; boolean success = true; for(int row = 0; row < N && success; ++row) { for(int col = 0; col < N; ++col) { if (sc.hasNextDouble()) { result[row][col] = sc.nextDouble(); } else { success = false; break; } } } if (!success) throw new RuntimeException("Fewer than the expected number of elements found."); //sc.close(); } return result; } /**Read a boolean array with size dynamically determined from input * Assumes properly formatted input * @param sc the Scanner to read from * @return the RxC array, where R & C are read from the source */ private static boolean[][] readBooleanArray(Scanner sc) { final int r = sc.nextInt(); final int c = sc.nextInt(); sc.nextLine(); // consume EOL boolean[][] result = new boolean[r][c]; boolean success = true; for(int row = 0; row < r && success; ++row) { final String s = sc.nextLine(); success = s.length() == c; if (!success) break; for(int col = 0; col < c; ++col) result[row][col] = s.charAt(col) == '1'; } if (!success) { throw new RuntimeException("Did not find the expected number of elements."); } //sc.close(); return result; } /** * prompt for char * @param prompt prompt to display * @return the entered char */ private static char inputChar(String prompt) { System.out.print(prompt); return sc.next(".").charAt(0); } /** * prompt for int, skipping any invalid preamble * @param prompt prompt to display * @return the entered int */ private static int inputInt(String prompt) { System.out.print(prompt); while (!sc.hasNextInt()) sc.next(); // discard non-int token int retVal = sc.nextInt(); sc.nextLine(); // dump CR from stream return retVal; } /** * prompt for unsigned int, skipping any invalid preamble * @param prompt prompt to display * @return the entered unsigned int */ private static int inputUnsignedInt(String prompt) { System.out.print(prompt); int result = -1; while (result < 0) { while (!sc.hasNextInt()) sc.next(); // discard non-int token result = sc.nextInt(); } sc.nextLine(); // dump CR from stream return result; } /** * prompt for a float, skipping any invalid preamble * @param prompt prompt to display * @return the entered float */ private static float inputFloat(String prompt) { System.out.print(prompt); while (!sc.hasNextFloat()) sc.next(); // discard non-matching token float retVal = sc.nextFloat(); sc.nextLine(); // dump CR from stream return retVal; } /** * prompt for a double, skipping any invalid preamble * @param prompt prompt to display * @return the entered double */ private static double inputDouble(String prompt) { System.out.print(prompt); while (!sc.hasNextDouble()) sc.next(); // discard non-matching token double retVal = sc.nextDouble(); sc.nextLine(); // dump CR from stream return retVal; } private static String inputLine(String prompt) // Simple prompt for a line of text { System.out.print(prompt); String result; // Key here is to discard empty lines (including \n's left in the buffer), but we make it a bit more robust to whitespace... do { result = sc.nextLine(); result = result.trim(); } while (result.length() == 0); return result; } private final static Scanner sc = new Scanner(System.in); }