import se.lth.cs.pt.io.*; import se.lth.cs.pt.clock.*; import java.util.*; import java.io.*; class Towns { public static void main(String[] args) { new Towns().run(); } List<String> towns = new LinkedList<String>(); StopWatch sw = new StopWatch(); Random rng = new Random(); void run() { inputTowns(); shuffleTowns(); query(); } void inputTowns() { Scanner s = getScanner(); while (s.hasNextLine()) { String town = s.nextLine(); if (town.length() > 0) { towns.add(town); } } } Scanner getScanner() { String filename = Keyboard.nextLine("Landskap: "); try { return new Scanner(new File(filename)); } catch (Exception e) { System.out.println("Det finns ingen sådan fil."); System.exit(1); return null; // för kompilatorns skull (vi kommer aldrig hit!) } } void shuffleTowns() { for (int k = 0; k < towns.size(); k++) { int index = rng.nextInt(towns.size()); towns.add(towns.remove(index)); } } void query() { for (String town : towns) { queryOne(town); } } void queryOne(String town) { sw.restart(); String guess = Keyboard.nextLine(scramble(town) + ": "); sw.stop(); if (guess.toUpperCase().equals(town.toUpperCase())) { System.out.printf("Rätt! (%.1f s)\n", sw.getValue()/1000.0); } else { System.out.println("Fel, rätt svar är " + town); } } String scramble(String str) { StringBuilder sb = new StringBuilder(str.toUpperCase()); for (int i = sb.length()-1; i > 0; i--) { int r = rng.nextInt(i+1); char saved = sb.charAt(i); sb.setCharAt(i,sb.charAt(r)); sb.setCharAt(r,saved); } return sb.toString(); } }
Vi skulle naturligtvis även kunna använda klasser, exempelvis:
import se.lth.cs.pt.io.*; import se.lth.cs.pt.clock.*; import java.util.*; import java.io.*; class Towns { public static void main(String[] args) { new Towns().run(); } List<Town> towns = new LinkedList<Town>(); double bestTime = Double.POSITIVE_INFINITY; double worstTime = 0; StopWatch sw = new StopWatch(); void run() { inputTowns(); test(); report(); } void inputTowns() { String filename = Keyboard.nextLine("Landskap: "); Scanner infile = null; try { infile = new Scanner(new File(filename)); } catch (Exception e) { System.out.println("Fel på indatafilen."); return; } for (;;) { String town = infile.nextLine(); if (town.length() == 0) { break; } towns.add(new Town(town)); } Collections.shuffle(towns); // standardklass (överkurs, se // annars shuffle() ovan). } void test() { for (Town town : towns) { askOne(town); } } void askOne(Town town) { sw.reset(); sw.start(); String guess = Keyboard.nextLine(town.getScrambled() + ": "); sw.stop(); double time = sw.getValue()/1000.0; worstTime = Math.max(worstTime, time); if (guess.toUpperCase().equals(town.getName().toUpperCase())) { bestTime = Math.min(bestTime, time); System.out.printf("Rätt! (%.1f s)\n", time); } else { System.out.println("Nix, rätt svar är " + town.getName()); } } void report() { System.out.printf("Bästa tid: %.1f s\n", bestTime); System.out.printf("Sämsta tid: %.1f s\n", worstTime); } } class Town { private String name; private String scrambled; public Town (String name) { this.name = name; scrambled = scramble(name); } public String getName() { return name; } public String getScrambled() { return scrambled; } private static Random rng = new Random(); private static String scramble(String name) { StringBuilder sb = new StringBuilder(name.toUpperCase()); for (int i = sb.length()-1; i > 0; i--) { int r = rng.nextInt(i); char tmp = sb.charAt(i); sb.setCharAt(i, sb.charAt(r)); sb.setCharAt(r, tmp); } return sb.toString(); } }
För att göra programmet lite kortare kan vi använda
klassen GameTemplate
för DotWindow
- vi
slipper då skriva en egen händelseloop.
När vi anropar run()
-operationen
i GameTemplate
kommer först setup()
att
anropas, och därefter anropas mouseClicked()
varje gång
användaren klickar med musen. Vi ärver
ett DotWindow
-attribut w
från GameTemplate
:
import se.lth.cs.pt.dotwindow.*; import se.lth.cs.pt.game.dotwindow.*; import java.util.*; class InversoGame extends GameTemplate { protected int tries = 0; protected int size = 3; protected int dotSize = 40; protected Color backgroundColor = new Color("#98BF7C"), lightColor = new Color("#CAFFA5"), darkColor = new Color("#627C4F"); public void setup() { w = new DotWindow(size, size, dotSize); w.useCircularDots(backgroundColor); w.checkMouse(true, false, false, false, false); randomize(); } private void randomize() { Random rng = new Random(); for (int x = 0; x < size; x++) { for (int y = 0; y < size; y++) { if (rng.nextBoolean()) { w.setDot(x,y,lightColor); } else { w.setDot(x,y,darkColor); } } } } public void mouseClicked(int xPos, int yPos) { tries++; for (int x = xPos-1; x <= xPos+1; x++) { for (int y = yPos-1; y <= yPos+1; y++) { if (isInside(x,y)) { w.setDot(x,y,oppositeOf(w.getDot(x,y))); } } } if (allLight()) { System.out.printf("Du klarade det på %d försök.\n", tries); quit(); } } private Color oppositeOf(Color color) { if (color.equals(lightColor)) { return darkColor; } return lightColor; } private boolean isInside(int x, int y) { return between(0,x,size) && between(0,y,size); } private boolean between(int min, int value, int max) { // Kollar om min <= value < max. Denna lite egendomliga // asymmetri är faktiskt något slags standard i C och Java. return min <= value && value < max; } private boolean allLight() { for (int x = 0; x < size; x++) { for (int y = 0; y < size; y++) { if (w.getDot(x,y).equals(darkColor)) { return false; } } } return true; } }
Vi kan starta programmet från följande huvudprogram:
class Inverso { public static void main(String[] args) { new Inverso().run(); } void run() { new InversoGame().run(); } }
eller bara:
class Inverso { public static void main(String[] args) { new InversoGame().run(); } }
Även här kan vi använda GameTemplate
-klassen för
DotWindow
, som vi stötte på i föregående uppgift:
class Battleship extends GameTemplate { Random rng = new Random(); Ship[][] board; int size; Color backgroundColor = new Color("#000668"), emptyColor = new Color("#171FB7"), aircraftCarrierColor = new Color("#C10811"), battleshipColor = new Color("#E44048"), cruiserColor = new Color("#FD7D83"), patrolBoatColor = new Color("#E1979A"), submarineColor = new Color("#BD8183"), missColor = new Color("#0B1292"); Ship[] ships = { new Ship(5, "an Aircraft Carrier", aircraftCarrierColor), new Ship(4, "a Battleship", battleshipColor), new Ship(3, "a Cruiser", cruiserColor), new Ship(2, "a Patrol Boat", patrolBoatColor), new Ship(3, "a Submarine", submarineColor) }; void setup() { int size = Keyboard.nextInt("Storlek på planen: "); board = new Ship[size][size]; w = new DotWindow(size,size,30); w.useCircularDots(backgroundColor); w.fillWith(emptyColor); placeShips(); w.checkMouse(true,false,false,false,false); } void placeShips() { for (Ship ship : ships) { ship.place(); } } void mouseClicked(int x, int y) { Ship s = board[x][y]; if (s != null) { s.hit(); board[x][y] = null; if (s.sunk()) { System.out.printf("You sank %s!\n", s.getName()); s.display(); } } else { w.setDot(x,y,missColor); } } class Ship { final int[] dx = {1, 0}; final int[] dy = {0, 1}; int size, originalSize; String name; Color color; int dir, x, y; Ship (int size, String name, Color color) { this.size = size; originalSize = size; this.name = name; this.color = color; } String getName() { return name; } void hit() { size--; } boolean sunk() { return size == 0; } void display() { for (int i = 0; i < originalSize; i++) { w.setDot(x + dx[dir]*i, y + dy[dir]*i, color); } } void place() { outer: for (;;) { dir = rng.nextInt(2); x = rng.nextInt(board.length - size*dx[dir]); y = rng.nextInt(board.length - size*dy[dir]); for (int i = 0; i < size; i++) { if (board[x + dx[dir]*i][y + dy[dir]*i] != null) { continue outer; } } for (int i = 0; i < size; i++) { board[x + dx[dir]*i][y + dy[dir]*i] = this; } return; } } } }
Uppgiften blir enklare att lösa om vi utnyttar det faktum att vi alltid bryter ner tal i grupper om 3 siffror:
import se.lth.cs.pt.io.*; class Translate { public static void main(String[] args) { new Translate().run(); } void run() { for (;;) { int number = Keyboard.nextInt("Mata in ett tal: "); if (number <= 0) { break; } System.out.println(translate(number)); } } final int TEN = 10, HUNDRED = 10*TEN, THOUSAND = 10*HUNDRED, MILLION = 1000*THOUSAND, BILLION = 1000*MILLION; final String[] TENS = {"tjugo", "trettio", "fyrtio", "femtio", "sextio", "sjuttio", "åttio", "nittio"}, TEENS = {"tio", "elva", "tolv", "tretton", "fjorton", "femton", "sexton", "sjutton", "arton", "nitton"}, DIGITS = {"", "ett", "två", "tre", "fyra", "fem", "sex", "sju", "åtta", "nio"}; String translate(int number) { StringBuilder text = new StringBuilder(); int billions = number / BILLION; if (billions > 0) { text.append(translateThreeDigits(billions) + "miljarder"); number %= BILLION; } int millions = number / MILLION; if (millions > 0) { text.append(translateThreeDigits(millions) + "miljoner"); number %= MILLION; } int thousands = number / THOUSAND; if (thousands > 0) { text.append(translateThreeDigits(thousands) + "tusen"); number %= THOUSAND; } text.append(translateThreeDigits(number)); return text.toString(); } String translateThreeDigits(int number) { StringBuilder text = new StringBuilder(); int hundreds = number / HUNDRED; if (hundreds > 0) { text.append(DIGITS[hundreds] + "hundra"); number %= HUNDRED; } int tens = number / TEN; int ones = number % TEN; if (tens == 1) { text.append(TEENS[number-10]); } else { if (tens > 1) { text.append(TENS[tens-2]); } if (ones > 0) { text.append(DIGITS[ones]); } } return text.toString(); } }
Operationen translate
innehåller ganska mycket
upprepad kod, vi kan parametrisera det som skiljer sig åt och skriva
ett underprogram istället:
String translate(int number) { StringBuilder text = new StringBuilder(); number = translateGroup(number, text, BILLION, "miljarder"); number = translateGroup(number, text, MILLION, "miljoner"); number = translateGroup(number, text, THOUSAND, "tusen"); number = translateGroup(number, text, 1, ""); return text.toString(); } int translateGroup(int number, StringBuilder text, int factor, String factorName) { int times = number / factor; if (times > 0) { text.append(translateThreeDigits(times) + factorName); } return number % factor; }