Tengo una pregunta bastante complicada para la que actualmente no puedo encontrar una respuesta:
Lo que tengo es una enumeración con x variantes como:
public enum Symbol { ROCK, PAPER, SCISSORS; }Se supone que esta enumeración debe extenderse por una, dos, ... variantes (como Piedra, Papel, Tijera, Spock, Lagarto) y estoy buscando una opción para almacenar el resultado de las combinaciones (en el ejemplo, quién está ganando ). Al igual que en Piedra, Papel, Tijeras, no puedo asignar un peso/valor a la enumeración que luego podría usarse para la comparación. El resultado de la comparación no se basa en la lógica. Lo que estoy haciendo actualmente es:
public Result calculateResult(Symbol hand2) { Symbol handP1 = this; Symbol handP2 = hand2; if (handP1 == Symbol.ROCK) { switch (handP2) { case SCISSORS: return Result.WIN; case ROCK: return Result.TIE; case PAPER: return Result.LOOSE; } ....Hago esto para cada opción posible para el primer elemento de la comparación (en el ejemplo hand1). Con 3 enumeraciones esto es bastante fácil y ordenado, pero con 4,5 o más se complica muy rápido. Usar if y else if para comparar tampoco es realmente adecuado. ¿Hay alguna forma mejor de manejar este problema?
He configurado un repl si quieres probarlo tú mismo: REPL Link
El uso de para o más opciones rompe la relación circular que podría tener con 3 opciones. Por favor vea aquí un ejemplo:
Gracias por cualquier consejo y sugerencia que pueda ayudarme a obtener una mejor solución.
Algo como esto podría ayudar
public enum Symbol { ROCK, SCISSORS, PAPER; private List<Symbol> beats; static { ROCK.beats = Arrays.asList(SCISSORS); SCISSORS.beats = Arrays.asList(PAPER); PAPER.beats = Arrays.asList(ROCK); } public Result calculateResult(Symbol hand2) { if (this.beats.contains(hand2)) return Result.WIN; if (hand2.beats.contains(this)) return Result.LOOSE; return Result.TIE; } }Algo como esto podría funcionar:
import java.util.Arrays; import java.util.List; public class SymbolGame { public enum Result { WIN,TIE,LOOSE; } public enum Symbol { ROCK, PAPER, SCISSORS; List<Symbol> winsFrom; public void setWinsFrom(Symbol... winsFrom) { this.winsFrom = Arrays.asList(winsFrom); } public Result calculate(Symbol other) { if(this==other) return Result.TIE; if (getWinsFrom().contains(other)) return Result.WIN; return Result.LOOSE; } } // somewhere you need to define the rules static { Symbol.ROCK.setWinsFrom(Symbol.SCISSORS); Symbol.PAPER.setWinsFrom(Symbol.ROCK); Symbol.SCISSORS.setWinsFrom(Symbol.PAPER); } public static void main(String[] args) { System.out.println("ROCK against PAPER:"+Symbol.ROCK.calculate(Symbol.PAPER)); System.out.println("PAPER against ROCK:"+Symbol.PAPER.calculate(Symbol.ROCK)); System.out.println("ROCK against ROCK:"+Symbol.ROCK.calculate(Symbol.ROCK)); } }Las enumeraciones tienen un ordinal asociado con cada valor, por lo que es una forma natural de indexar en una matriz bidimensional. Esto también es bueno porque es bastante similar a cómo puedes escribir todas las posibilidades en una hoja de papel. Es fácil de extender simplemente agregando más columnas y filas:
// @formatter:off private static final Result[][] RESULTS = { // v versus > ROCK PAPER SCISSORS /* ROCK. */ { TIE, LOSE, WIN }, /* PAPER */ { WIN, TIE, LOSE }, /* SCISSORS */ { LOSE, WIN, TIE }, } // @formatter:on public Result calculateResult(Symbol hand1, Symbol hand2) { return RESULTS[hand1.ordinal()][hand2.ordinal()] } Muchos IDE admiten el @formatter:on/off para evitar que vuelvan a formatear su tabla.
IMPORTANTE : Java efectivo y la API de Java en sí dicen que no se use ordinal() debido a los peligros de cambios futuros (grabación, inserción de nuevos valores) que causan errores sutiles. También es cierto aquí: si solo cambia la enumeración para que sea ROCK,SCISSORS,PAPER , entonces este formulario de tabla fallará. Como mínimo, coloque este código dentro de Symbol para que quede claro que necesita actualizarse y escriba buenas pruebas unitarias.