diff --git a/src/main/java/schule/ngb/zm/Constants.java b/src/main/java/schule/ngb/zm/Constants.java index 74046ff..eea343c 100644 --- a/src/main/java/schule/ngb/zm/Constants.java +++ b/src/main/java/schule/ngb/zm/Constants.java @@ -8,16 +8,17 @@ import java.awt.Cursor; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; +import java.util.Arrays; import java.util.Random; import java.util.function.DoubleUnaryOperator; /** - * Basisklasse für die meisten Objekte der Zeichemaschine, die von Nutzern + * Basisklasse für die meisten Objekte der Zeichenmaschine, die von Nutzern * erweitert werden können. *

* Die Konstanten stellen viele Funktionen zur einfachen Programmierung bereit * und enthält auch einige dynamische Werte, die von der Zeichenmaschine laufend - * aktuell gehalten werden (beispielsweise {@link #runtime}. + * aktuell gehalten werden (beispielsweise {@link #runtime}). *

* Für die Implementierung eigener Klassen ist es oft hilfreich von * {@code Constants} zu erben, um die Methoden und Konstanten einfach im @@ -64,13 +65,49 @@ public class Constants { /** * Patchversion der Zeichenmaschine. */ - public static final int APP_VERSION_REV = 22; + public static final int APP_VERSION_REV = 31; /** * Version der Zeichenmaschine als Text-String. */ public static final String APP_VERSION = APP_VERSION_MAJ + "." + APP_VERSION_MIN + "." + APP_VERSION_REV; + /** + * Gibt an, ob die Zeichenmaschine unter macOS gestartet wurde. + */ + public static final boolean MACOS; + + /** + * Gibt an, ob die Zeichenmaschine unter Windows gestartet wurde. + */ + public static final boolean WINDOWS; + + /** + * Gibt an, ob die Zeichenmaschine unter Linux gestartet wurde. + */ + public static final boolean LINUX; + + static { + final String name = System.getProperty("os.name"); + + if( name.contains("Mac") ) { + MACOS = true; + WINDOWS = false; + LINUX = false; + } else if( name.contains("Windows") ) { + MACOS = false; + WINDOWS = true; + LINUX = false; + } else if( name.equals("Linux") ) { // true for the ibm vm + MACOS = false; + WINDOWS = false; + LINUX = true; + } else { + MACOS = false; + WINDOWS = false; + LINUX = false; + } + } /** * Standardbreite eines Zeichenfensters. @@ -1280,6 +1317,26 @@ public class Constants { return getRandom().nextGaussian(); } + /** + * Wählt ein zufälliges Element aus dem Array aus. + * + * @param values Ein Array mit Werten, die zur Auswahl stehen. + * @return Ein zufälliges Element aus dem Array. + */ + public static final int choice( int[] values ) { + return values[random(0, values.length - 1)]; + } + + /** + * Wählt ein zufälliges Element aus dem Array aus. + * + * @param values Ein Array mit Werten, die zur Auswahl stehen. + * @return Ein zufälliges Element aus dem Array. + */ + public static final double choice( double[] values ) { + return values[random(0, values.length - 1)]; + } + /** * Wählt ein zufälliges Element aus dem Array aus. * @@ -1291,6 +1348,69 @@ public class Constants { return values[random(0, values.length - 1)]; } + /** + * Wählt die angegebene Anzahl Elemente aus dem Array aus. + * + * @param values Ein Array mit Werten, die zur Auswahl stehen. + * @param n Anzahl der auszuwählenden Elemente. + * @param unique Bei {@code true} werden Elemente im Array nur maximal + * einmal ausgewählt (Ziehen ohne Zurücklegen). + * @return Ein zufälliges Element aus dem Array. + * @throws IllegalArgumentException Wenn {@code unique == true} und + * {@code values.length < n}, also nicht + * genug Werte zur Wahl stehen. + */ + public static final int[] choice( int[] values, int n, boolean unique ) { + if( unique && values.length < n ) + throw new IllegalArgumentException( + String.format("Need at least <%d> values to choose <%d> unique values (<%d> given).", n, n, values.length) + ); + + int[] result = new int[n]; + int[] valuesCopy = Arrays.copyOf(values, values.length); + for( int i = 0; i < n; i++ ) { + int j = random(0, valuesCopy.length-1); + int l = valuesCopy.length-1; + + result[i] = valuesCopy[j]; + valuesCopy[j] = valuesCopy[l]; + valuesCopy[l] = result[i]; + + if( unique ) + valuesCopy = Arrays.copyOf(valuesCopy, l); + } + return result; + } + + /** + * Wählt die angegebene Anzahl Elemente aus dem Array aus. + * + * @param values Ein Array mit Werten, die zur Auswahl stehen. + * @param n Anzahl der auszuwählenden Elemente. + * @return Ein zufälliges Element aus dem Array. + */ + public static final double[] choice( double[] values, int n, boolean unique ) { + if( unique && values.length < n ) + throw new IllegalArgumentException( + String.format("Need at least <%d> values to choose <%d> unique values (<%d> given).", n, n, values.length) + ); + + double[] result = new double[n]; + double[] valuesCopy = Arrays.copyOf(values, values.length); + for( int i = 0; i < n; i++ ) { + int j = random(0, valuesCopy.length-1); + int l = valuesCopy.length-1; + + result[i] = valuesCopy[j]; + valuesCopy[j] = valuesCopy[l]; + valuesCopy[l] = result[i]; + + if( unique ) + valuesCopy = Arrays.copyOf(valuesCopy, l); + } + return result; + } + /** * Wählt die angegebene Anzahl Elemente aus dem Array aus. * @@ -1299,12 +1419,26 @@ public class Constants { * @param Datentyp der Elemente. * @return Ein zufälliges Element aus dem Array. */ - public static final T[] choice( T[] values, int n ) { - Object[] result = new Object[n]; + public static final T[] choice( T[] values, int n, boolean unique ) { + if( unique && values.length < n ) + throw new IllegalArgumentException( + String.format("Need at least <%d> values to choose <%d> unique values (<%d> given).", n, n, values.length) + ); + + T[] result = Arrays.copyOf(values, n); + T[] valuesCopy = Arrays.copyOf(values, values.length); for( int i = 0; i < n; i++ ) { - result[i] = choice(values); + int last = valuesCopy.length-1; + int j = random(0, last); + + result[i] = valuesCopy[j]; + valuesCopy[j] = valuesCopy[last]; + valuesCopy[last] = result[i]; + + if( unique ) + valuesCopy = Arrays.copyOf(valuesCopy, last); } - return (T[]) result; + return result; } /** @@ -1474,7 +1608,7 @@ public class Constants { } public static final double asDouble( char value ) { - return (double) value; + return value; } public static final double asDouble( byte value ) { diff --git a/src/test/java/schule/ngb/zm/ConstantsTest.java b/src/test/java/schule/ngb/zm/ConstantsTest.java index 529a696..d332db7 100644 --- a/src/test/java/schule/ngb/zm/ConstantsTest.java +++ b/src/test/java/schule/ngb/zm/ConstantsTest.java @@ -2,6 +2,9 @@ package schule.ngb.zm; import org.junit.jupiter.api.Test; +import java.util.Arrays; +import java.util.stream.IntStream; + import static org.junit.jupiter.api.Assertions.*; class ConstantsTest { @@ -169,4 +172,30 @@ class ConstantsTest { assertTrue(0.0 <= d && 1.0 >= d, "Noise should be in Range 0 to 1. Was <" + d + ">."); } + @Test + void choice() { + int[] values = IntStream.range(0,1000).toArray(); + int[] choosen = Constants.choice(values, 1, false); + assertEquals(1, choosen.length); + assertTrue(Arrays.stream(values).anyMatch((i) -> i == choosen[0])); + + int[] choosen4 = Constants.choice(values, 2000, false); + assertEquals(2000, choosen4.length); + + int[] choosen2 = Constants.choice(values, 400, false); + assertEquals(400, choosen2.length); + assertTrue(Arrays.stream(choosen2).allMatch((i) -> Arrays.binarySearch(values, i)>=0 )); + + int[] choosen3 = Constants.choice(values, 999, true); + assertEquals(999, choosen3.length); + assertTrue(Arrays.stream(choosen3).allMatch((i) -> Arrays.binarySearch(values, i)>=0 )); + for( int i = 0; i < choosen3.length; i++ ) { + for( int j = i+1; j < choosen3.length; j++ ) { + assertNotEquals(choosen3[i], choosen3[j], "Item "+i+" also found at index "+j); + } + } + + assertThrows(IllegalArgumentException.class, () -> Constants.choice(values, 2000, true)); + } + }