Untitled
unknown
plain_text
a year ago
7.1 kB
32
Indexable
package de.uni_bremen.pi2;
// INVALID, VALID und SOLVED können direkt verwendet werden.
import static de.uni_bremen.pi2.InsectHotel.Result.*;
public class InsectHotel
{
/**
* Die drei möglichen Ergebnisse der Methode {@link #check}.
* INVALID: Die aktuelle Teilbelegung des Hotels ist bereits ungültig.
* VALID: Die aktuelle Teilbelegung des Hotels ist gültig.
* SOLVED: Das Hotel ist voll belegt und gültig.
*/
enum Result {INVALID, VALID, SOLVED}
/**
* Die Methode füllt ein zum Teil belegtes Hotel mit weiteren
* Einträgen auf. Dabei werden die weiteren Einträge der Reihe nach
* von oben links nach unten rechts eingetragen (horizontal zuerst).
* Dabei bleiben die existierenden Einträge erhalten und die neuen
* Einträge ersetzen nur die Lücken (' ').
* @param hotel Das zum Teil belegte Hotel. Wird nicht verändert.
* {@see #solve}.
* @param entries Eine Folge aus 'O' und 'X', die in die Lücken in
* {@code hotel} eintragen wird.
* @return Ein Hotel, bei dem {@code entries} in die Lücken von
* {@code hotel} eingesetzt wurde, zumindest so weit, wie
* {@code entries} gereicht hat. Da {@code hotel} nicht geändert
* werden darf, ist dies ein neues Objekt.
*/
static String[] fill(final String[] hotel, final String entries)
{
String[] newHotel = new String[hotel.length]; // Cloned array
int entryIndex = 0; // To track the current position in the entries string
for (int i = 0; i < hotel.length; i++) {
StringBuilder newRow = new StringBuilder(hotel[i]); // Work with a mutable StringBuilder
for (int j = 0; j < newRow.length(); j++) {
if (newRow.charAt(j) == ' ' && entryIndex < entries.length()) {
newRow.setCharAt(j, entries.charAt(entryIndex++)); // Replace space with the next entry
}
}
newHotel[i] = newRow.toString(); // Convert StringBuilder back to String and assign to the new array
}
return newHotel; // Return the newly filled hotel
}
/**
* Die Methode überprüft, ob ein Hotel gültig (teil-) belegt ist.
* Es dürfen horizontal und vertikal maximal jeweils zwei gleiche
* Einträge ('O' bzw. 'X') aufeinander folgen. In jeder voll
* ausgefüllten Zeile und Spalte müssen gleich viele Einträge beider
* Sorten stehen.
* @param hotel Das zum Teil belegte Hotel. {@see #solve}.
* @return
* INVALID: Wenn eine der Anforderungen nicht erfüllt ist – oder
* besser – auch mit späteren Eintragungen nicht mehr erfüllt
* werden kann.
* VALID: Bisher ist keine Anforderung verletzt und mit weiteren
* Eintragungen könnte noch eine gültige Lösung entstehen.
* SOLVED: Das Hotel ist vollständig belegt und keine der
* Anforderungen ist verletzt.
*/
static Result check(final String[] hotel) {
boolean isCompletelyFilled = true;
for (String row : hotel) {
if (!isRowValid(row)) {
return INVALID;
}
if (row.contains(" ")) {
isCompletelyFilled = false;
}
}
for (int col = 0; col < hotel[0].length(); col++) {
if (!isColumnValid(hotel, col)) {
return INVALID;
}
}
return isCompletelyFilled ? SOLVED : VALID;
}
private static boolean isRowValid(String row) {
return isLineValid(row) && isBalanced(row);
}
private static boolean isColumnValid(String[] hotel, int colIndex) {
StringBuilder column = new StringBuilder();
for (String row : hotel) {
column.append(row.charAt(colIndex));
}
return isLineValid(column.toString()) && isBalanced(column.toString());
}
private static boolean isLineValid(String line) {
int count = 1;
char lastChar = line.charAt(0);
for (int i = 1; i < line.length(); i++) {
char currentChar = line.charAt(i);
if (currentChar == lastChar && currentChar != ' ') {
count++;
if (count > 2) {
return false;
}
} else {
lastChar = currentChar;
count = 1;
}
}
return true;
}
private static boolean isBalanced(String line) {
if (line.contains(" ")) {
return true; // Skip balance checking if the line is not fully filled
}
long countX = line.chars().filter(ch -> ch == 'X').count();
long countO = line.chars().filter(ch -> ch == 'O').count();
return countX == countO;
}
/**
* Die Methode bekommt ein zum Teil belegtes Hotel übergeben und gibt
* ein gültig voll belegtes Hotel zurück, wenn dies möglich ist. Die
* Details, was "gültig" bedeutet, stehen auf dem Übungsblatt.
* @param hotel Das zum Teil belegte Hotel. Es besteht aus den Zeichen
* ' ', 'O' und 'X'. Alle Zeilen müssen dieselbe Länge
* haben. Weder der Parameter noch eine seiner Zeilen
* dürfen null sein. Wenn von diesen Vorgaben, abgewichen
* wird, ist das Verhalten undefiniert.
* @return Ein gültig voll belegtes Hotel oder null, wenn es keine
* gültige Belegung gibt.
*/
public static String[] solve(final String[] hotel) {
String[] solution = new String[hotel.length];
System.arraycopy(hotel, 0, solution, 0, hotel.length); // Copy hotel to solution to preserve original
if (solveRecursive(solution, 0)) {
return solution;
} else {
return null; // No solution found
}
}
private static boolean solveRecursive(String[] hotel, int cellIndex) {
if (cellIndex == hotel.length * hotel[0].length()) { // Check if we're past the last cell
return check(hotel) == Result.SOLVED; // Check if the solution is complete and valid
}
int row = cellIndex / hotel[0].length();
int col = cellIndex % hotel[0].length();
String currentRow = hotel[row];
if (currentRow.charAt(col) != ' ') { // Skip already filled cells
return solveRecursive(hotel, cellIndex + 1);
}
char[] candidates = {'X', 'O'};
for (char candidate : candidates) {
hotel[row] = currentRow.substring(0, col) + candidate + currentRow.substring(col + 1);
if (check(hotel) != Result.INVALID) {
if (solveRecursive(hotel, cellIndex + 1)) {
return true;
}
}
hotel[row] = currentRow; // Reset the row after backtracking
}
return false;
}
}Editor is loading...
Leave a Comment