我得到了这个练习:


构建一个游戏,其中两个玩家在具有不同策略的剪刀石头布游戏中竞争。谁会赢得更多回合?规则:


剪刀打败纸
剪刀打败石
剪刀打败石
如果两个玩家选择相同,则该回合计为平局。

实施两个玩家:


玩家A总是选择纸张
玩家B随机选择

游戏包括以上两名选手进行100轮比赛。程序的
输出应如下所示:

"Player A wins 31 of 100 games"
"Player B wins 37 of 100 games"
"Tie: 32 of 100 games"



这是我的解决方案:

动作:

import java.util.EnumMap;
import java.util.Map;

/**
 * The moves of a {@link Game}
 * 
 * @author ms
 *
 */
public enum Move {

    ROCK, PAPER, SCISSORS;

    /**
     * Holds the moves a move beats
     */
    private static final Map<Move, Move>    beats   = new EnumMap<>(Move.class);

    // init the beats
    static {
        beats.put(ROCK, SCISSORS);
        beats.put(PAPER, ROCK);
        beats.put(SCISSORS, PAPER);
    }

    /**
     * Returns the move this move beats
     * 
     * @param m
     *            The current move
     * @return The move this move beats
     */
    public static Move beats(final Move m) {
        return beats.get(m);
    }

}



玩家:

/**
 * The superclass of all players
 * 
 * @author ms
 *
 */
public abstract class Player {

    /**
     * Generates the next move
     * 
     * @return the next move
     */
    public abstract Move getNextMove();

}



/**
 * A player that always returns a {@link Move#PAPER} move
 * 
 * @author ms
 *
 */
public class PaperPlayer extends Player {

    @Override
    public Move getNextMove() {
        return Move.PAPER;
    }

}



import java.util.Random;

/**
 * A player that always returns a random move
 * 
 * @author ms
 *
 */
public class RandomPlayer extends Player {

    /**
     * Caches all values of {@link Move} for the random generator
     */
    private static final Move[] moves   = Move.values();

    /**
     * The random number generator used; created once and then cached
     */
    private final Random        generator;

    public RandomPlayer() {
        generator = new Random();
    }

    @Override
    public Move getNextMove() {
        return moves[generator.nextInt(moves.length)];
    }

}



游戏:

import java.lang.invoke.MethodHandles;
import java.util.EnumMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A Game of rock-scissor-paper
 * 
 * @author ms
 *
 */
public class Game {

    private static final int    NUMBER_OF_GAMES = 100;

    private static final Logger logger          = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

    /**
     * An enum encapsulating the results of one move which also holds the strings needed for
     * generating the output
     * 
     * @author ms
     *
     */
    enum Result {
        A_WINS("Player A wins "), B_WINS("Player B wins "), TIE("Tie: ");

        private final String    output;

        private Result(String output) {
            this.output = output;
        }

        public String getOutput() {
            return output;
        }
    }

    /**
     * @param args
     */
    public static void main(final String[] args) {

        final Player playerA = new PaperPlayer();
        final Player blayerB = new RandomPlayer();
        final Map<Result, Integer> results = new EnumMap<>(Result.class);

        initResults(results);
        playGame(playerA, blayerB, results);
        printResults(results);
    }

    /**
     * Initialize the {@code results} map with 0 values
     * 
     * @param results
     *            the inialized results map
     */
    private static void initResults(final Map<Result, Integer> results) {
        for (final Result r : Result.values()) {
            results.put(r, 0);
        }
    }

    /**
     * Plays {@link #NUMBER_OF_GAMES} rounds of the game
     * 
     * @param playerA
     *            A player
     * @param blayerB
     *            A player
     * @param results
     *            The results are stored here
     * @see #playOneRoundOfTheGame(Player, Player, Map)
     */
    private static void playGame(final Player playerA, final Player blayerB, final Map<Result, Integer> results) {
        for (int i = 0; i < NUMBER_OF_GAMES; i++ ) {
            playOneRoundOfTheGame(playerA, blayerB, results);
        }
    }

    /**
     * Plays one round of the game
     * 
     * @param playerA
     *            A player
     * @param blayerB
     *            A player
     * @param results
     *            The results are stored here
     * @see #playGame(Player, Player, Map)
     */
    private static void playOneRoundOfTheGame(final Player playerA, final Player blayerB,
            final Map<Result, Integer> results) {
        final Move moveA = playerA.getNextMove();
        final Move moveB = blayerB.getNextMove();
        final Result result = evaluateMoves(moveA, moveB);
        logger.debug("A: {}, B: {}. result: {}", moveA, moveB, result);
        results.put(result, results.get(result) + 1);
    }

    /**
     * Evaluates one round of the game
     * 
     * @param moveA
     *            The move of one player
     * @param moveB
     *            The move of another player
     * @return The {@link Result}
     */
    static Result evaluateMoves(final Move moveA, final Move moveB) {
        final Result result;
        if (Move.beats(moveA) == moveB) {
            result = Result.A_WINS;
        }
        else {
            if (Move.beats(moveB) == moveA) {
                result = Result.B_WINS;
            }
            else {
                result = Result.TIE;
            }
        }
        return result;
    }

    /**
     * Prints the results
     * 
     * @param results
     *            The results
     */
    private static void printResults(final Map<Result, Integer> results) {
        for (final Result r : Result.values()) {
            System.out.printf("%s %d of %d games%n", r.getOutput(), results.get(r), NUMBER_OF_GAMES);
        }
    }

}



测试:

import java.util.Arrays;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;

import Game.Result;

import static org.assertj.core.api.Assertions.*;

/**
 * @author ms
 *
 */
@RunWith(Parameterized.class)
public class GameTest {

    @Parameters
    public static Iterable<Object[]> testData() {
        return Arrays.asList(new Object[][] { { Move.ROCK, Move.ROCK, Result.TIE },
                { Move.ROCK, Move.PAPER, Result.B_WINS }, { Move.ROCK, Move.SCISSORS, Result.A_WINS },
                { Move.PAPER, Move.PAPER, Result.TIE }, { Move.PAPER, Move.ROCK, Result.A_WINS },
                { Move.PAPER, Move.SCISSORS, Result.B_WINS }, { Move.SCISSORS, Move.SCISSORS, Result.TIE },
                { Move.SCISSORS, Move.ROCK, Result.B_WINS }, { Move.SCISSORS, Move.PAPER, Result.A_WINS }, });
    }

    @Parameter(0)
    public Move     moveA;

    @Parameter(1)
    public Move     moveB;

    @Parameter(2)
    public Result   expectedResult;

    /**
     * Test method for
     * {@link Game#evaluateMoves(Move, Move)}
     * .
     */
    @Test
    public void testEvaluateMoves() throws Exception {
        assertThat(Game.evaluateMoves(moveA, moveB)).isEqualTo(expectedResult);
    }

}




游戏逻辑(一拍一拍)保持一体位置(Move的
定义),可以轻松地将规则替换为例如
剪刀石头布-蜥蜴-spock

缺点:


组件的命名感觉太琐碎了
主循环在Game.playGame中可能可以使用Java8转换为Streams / Lambdas,但这会使收集结果更加困难

仅测试移动的评估,因为这是唯一的平凡的代码

#1 楼


但是我仍然对Java8 / Streams解决方案感兴趣。 – @MartinSchröder


嘿...这是表演时间!

Move

enum Move {
    ROCK, PAPER, SCISSORS;

    boolean beats(Move another) {
        switch (this) {
        case ROCK:
            return another == SCISSORS;
        case PAPER:
            return another == ROCK;
        case SCISSORS:
            return another == PAPER;
        // note: see alternative below
        default:
            throw new IllegalStateException();
        }
        // alternatively, just throw here without the default case
        // throw new IllegalStateException();
    }
}


如@tim和@OldCurmudgeon所建议,如果Move知道自己是否击败了另一个Move,则更好,而不是将其委托给static方法。但是,我的方法有所不同,原因是要使用switch语句而不是依赖static Map,这有两个原因:
任何优秀的IDE都会建议您为所有case值添加enum

如果Lizard和Spock拜访了您的游戏,则Map解决方案将需要大量的重新设计,相反,对于初学者来说,重构switch语句以谨慎地照顾它们至少更容易些。好的IDE还会提示您为新的throw值添加IllegalStateException子句,并且仍然会引发异常。

case

enum Player implements Iterator<Move> {
    A() {
        @Override
        public Move next() {
            return Move.PAPER;
        }
    }, B() {
        @Override
        public Move next() {
            return Move.values()[GENERATOR.nextInt(Move.values().length)];
        }
    };

    private static final Random GENERATOR = new Random();

    @Override
    public boolean hasNext() {
        return true;
    }

    @Override
    public void remove() {
        throw new UnsupportedOperationException();
    }
}


@OldCurmudgeon的答案实际上是使用enum接口对此实现进行了修订,但是由于某种原因,它不再存在。无论如何,我选择将其建模为Player,因为我们知道RPS中通常只有两个参与者(打折所有硬核MMORPS-我刚刚弥补了这一点),并且在问题中也对此进行了明确定义。

我也可以在常见的Iterator方法实现中执行enum,但是我想我将使用一种替代方法进行更改。另一点需要注意的是switch方法中的next()throw -ing:这只是一个好形式。

UnsupportedOperationException()

private static final class Round {

    private final Move aMove;
    private final Move bMove;

    private Round(Move aMove, Move bMove) {
        this.aMove = aMove;
        this.bMove = bMove;
    }

    public Optional<Player> getWinner() {
        return aMove == bMove ? Optional.empty() :
            Optional.of(aMove.beats(bMove) ? Player.A : Player.B);
    }

    public static Round of(Player aPlayer, Player bPlayer) {
        if (aPlayer.hasNext() && bPlayer.hasNext()) {
            return new Round(aPlayer.next(), bPlayer.next());
        }
        throw new NoSuchElementException("No more moves by player "
                + (aPlayer.hasNext() ? Player.B : Player.A));
    }
}


现在,这是我在建模上有显着差异的地方,也是第一个Java 8功能remove()出现的地方。韩元。这意味着给定一个Round,一个人不知道什么是导致结果的输入。缺点是:


必须将Optional及其表示形式分别建模为Result(请参阅Result / PlayerResult / PaperPlayer)。


更多的代码,如果我想更改播放器的策略,名称或将外部输入映射到移动中,则需要更改更多的地方。


一旦捕获结果,就会丢失输入。


随后,您将无法重玩游戏。
由于无法重新计算分数,因此也很容易躺在代码中。



因此,我继续捕捉了游戏回合的输入,简称为Result.A_WINS。它捕获的全部都是动作,然后调用RandomPlayer然后应用上述函数给我们一个Result.B_WINS。赢了,还是有平局。这使我可以方便地取消使用Round和显式执行getWinner() -checks的工作,或者不必为三值Optional<Player>添加代码来模拟平局场景。我将在稍后再讨论这一点,如果您考虑一下,平局情况本身也是两个玩家都不获胜的结果。因此,在找出游戏中的平局数目时,由于可以计算出值,因此并没有硬性要求以平局表示形式存储。

Optional内部进行了轻度的检查null方法,以充分利用实现null接口的enum类。

解决方案的另一件事:对每个static值使用of()实际上只是显示结果时要使用的必需前缀。因此,如果需求发生变化,则几乎没有灵活性:名称或模式的更改。

Player

public final class Game {

    private static final int NUMBER_OF_GAMES = 100;
    private static final Logger logger = LoggerFactory.getLogger(Game.class);

    private static Map<Player, Long> play() {
        return Stream.generate(() -> Round.of(Player.A, Player.B))
                .limit(NUMBER_OF_GAMES)
                .map(Round::getWinner)
                .filter(Optional::isPresent)
                .collect(Collectors.groupingBy(Optional::get,
                        () -> new EnumMap<>(Player.class), Collectors.counting()));
    }

    public static void main(String[] args) {
        Map<Player, Long> results = Game.play();
        int wins = results.values().stream().mapToInt(Long::intValue).sum();
        results.forEach((k, v) -> logger.info("Player {} wins {} of {} games", k, v,
                Integer.valueOf(NUMBER_OF_GAMES)));
        logger.info("Tie: {} of {} games", Integer.valueOf(NUMBER_OF_GAMES - wins),
                Integer.valueOf(NUMBER_OF_GAMES));
    }
}




Iterator为我们提供了output的无限连续流对我们来说,是基于调用Result构造的。
这可以通过调用Game来限制。
然后我们必须通过调用Stream.generate() -ping来查询每个Round的优胜者,方法是参考其Round.of(Player, Player)方法参考。
现在,由于这实际上是一个limit(NUMBER_OF_GAMES)包装器,因此我们可以通过调用Round方法(同样是另一个方法参考)来调用map()
最后,我们通过使用适当的参数执行getWinner来赢家,记住要先在Optional包装器上调用filter()才能访问底层获胜的isPresent()。我们可以在致电collect()之后首先计算获胜次数。这是通过在调用groupingBy()时返回的get()上执行Optional来完成的。要显示所需的结果,我们只需要在Player上执行main()并相应地“消耗”键和值即可。最后一行显示了我们的联系数量。

评论


\ $ \ begingroup \ $
很好。只需注意:我将移动默认值:抛出新的IllegalStateException();如果您添加了新的枚举值,则将收到警告(无论如何您都会获得异常)。
\ $ \ endgroup \ $
– maaartinus
15年5月13日在0:19

\ $ \ begingroup \ $
@maaartinus不错的建议,将包括在其中。:)
\ $ \ endgroup \ $
– h.j.k.
2015年5月13日,0:53

\ $ \ begingroup \ $
我特别喜欢您使用Player工具实现Iterator 。我尝试了这条路线,但无法找到一种方法来加入两个流。现在,我知道如何做到这一点。
\ $ \ endgroup \ $
–OldCurmudgeon
15年5月13日在8:17



\ $ \ begingroup \ $
如果Round在单独的文件中,则不能为private static。
\ $ \ endgroup \ $
–马丁·施罗德(MartinSchröder)
15年5月18日在13:18

\ $ \ begingroup \ $
另一个要求:我需要测试。目前,我可以测试Move,但是显然有人希望测试Round和Game。任何想法如何添加测试?
\ $ \ endgroup \ $
–马丁·施罗德(MartinSchröder)
15年5月18日在13:19



#2 楼

我喜欢您的代码,它的代码大部分都可读性强且结构良好。
static Result evaluateMoves(final Move moveA, final Move moveB) {
    if (Move.beats(moveA) == moveB) {
        return Result.A_WINS;
    } else if (Move.beats(moveB) == moveA) {
        return Result.B_WINS;
    } else {
        return Result.TIE;
    }
}


我也会考虑重组evaluateMoves枚举,以便您可以编写更好的if-elseif。这也将允许添加比石头,纸张和石头更容易的其他对象。将其移至调用类,或者-为获得更清晰的代码-创建单独的if-else-if类,其中包含当前与Move混合在一起的所有代码(例如初始化,添加等)。像if (moveA.beats(moveB))这样的东西会比playOneRoundOfTheGame好很多。

评论


\ $ \ begingroup \ $
您可以省略其他。
\ $ \ endgroup \ $
– Heslacher
15年5月12日在9:38



\ $ \ begingroup \ $
您也可以只对前两个条件使用两个单if语句,因为无论如何,当条件为真时从方法中返回,然后@heslacher会说:省略最后一个。这样就变成:如果(a)返回a_win,如果(b)返回b_win,则返回平局。
\ $ \ endgroup \ $
–孩子钻石
15年5月12日在10:04



\ $ \ begingroup \ $
谢谢。是的,Move.beats应该不是静态的。但是我仍然对Java8 / Streams解决方案感兴趣。
\ $ \ endgroup \ $
–马丁·施罗德(MartinSchröder)
15年5月12日在10:23

#3 楼

这是写得很好并且经过深思熟虑的代码。

我会回应其他人的建议。


使您的Move枚举具有beats方法。
使一个可以维持结果的Results对象。

这里是我修补后剩下的内容。因此,这可能不是一个理想的解决方案,但这是一个适度的流版本。 >
大多数流式传输是通过Play方法实现的。

/**
 * The moves of a {@link Game}
 *
 * @author ms
 *
 */
public enum Move {

    ROCK, PAPER, SCISSORS;

    /**
     * Holds the moves a move beats
     */
    private static final Map<Move, Move> beats = new EnumMap<>(Move.class);

    // init the beats
    static {
        beats.put(ROCK, SCISSORS);
        beats.put(PAPER, ROCK);
        beats.put(SCISSORS, PAPER);
    }

    /**
     * Returns true if this move beats
     *
     * @param other move to compare with.
     * 
     * @return true if this move beats the other.
     */
    public boolean beats(Move other) {
        return beats.get(this) == other;
    }
}

/**
 * The superclass of all players
 *
 * @author ms
 *
 */
public abstract static class Player {

    /**
     * Generates the next move
     *
     * @return the next move
     */
    public abstract Move getNextMove();

}

/**
 * A player that always returns a {@link Move#PAPER} move
 *
 * @author ms
 *
 */
public static class PaperPlayer extends Player {

    @Override
    public Move getNextMove() {
        return Move.PAPER;
    }

}

/**
 * A player that always returns a random move
 *
 * @author ms
 *
 */
public static class RandomPlayer extends Player {

    /**
     * Caches all values of {@link Move} for the random generator
     */
    private static final Move[] moves = Move.values();

    /**
     * The random number generator used; created once and then cached
     */
    private final Random generator;

    public RandomPlayer() {
        generator = new Random();
    }

    @Override
    public Move getNextMove() {
        return moves[generator.nextInt(moves.length)];
    }

}

/**
 * A Game of rock-scissor-paper
 *
 * @author ms
 *
 */
public static class Game {

    private static final int NUMBER_OF_GAMES = 100;

    /**
     * An enum encapsulating the results of one move which also holds the strings needed for generating the output
     *
     * @author ms
     *
     */
    enum Result {

        A_WINS("Player A wins "), B_WINS("Player B wins "), TIE("Tie: ");

        private final String output;

        private Result(String output) {
            this.output = output;
        }

        public String getOutput() {
            return output;
        }
    }

    private static class Results {

        final ConcurrentMap<Result, Integer> results = new ConcurrentHashMap<>(new EnumMap<>(Result.class));

        private void result(Result result) {
            // Add one (to 0 if it's not present.
            results.put(result, results.computeIfAbsent(result, x -> 0) + 1);
        }

        @Override
        public String toString() {
            StringBuilder s = new StringBuilder();
            for (final Result r : Result.values()) {
                s.append(r.getOutput())
                        .append(results.get(r)).append(" of ")
                        .append(NUMBER_OF_GAMES)
                        .append("\r\n");
            }
            return s.toString();
        }
    }

    /**
     * @param args
     */
    public static void main(final String[] args) {

        final Player playerA = new PaperPlayer();
        final Player blayerB = new RandomPlayer();
        final Results results = new Results();

        playGame(playerA, blayerB, results);
        System.out.println(results);
    }

    /**
     * Plays {@link #NUMBER_OF_GAMES} rounds of the game
     *
     * @param playerA A player
     * @param blayerB A player
     * @param results The results are stored here
     * @see #playOneRoundOfTheGame(Player, Player, Map)
     */
    private static void playGame(final Player playerA, final Player blayerB, Results results) {
        for (int i = 0; i < NUMBER_OF_GAMES; i++) {
            playOneRoundOfTheGame(playerA, blayerB, results);
        }
    }

    /**
     * Plays one round of the game
     *
     * @param playerA A player
     * @param blayerB A player
     * @param results The results are stored here
     * @see #playGame(Player, Player, Map)
     */
    private static void playOneRoundOfTheGame(final Player playerA, final Player blayerB,
            Results results) {
        final Move moveA = playerA.getNextMove();
        final Move moveB = blayerB.getNextMove();
        final Result result = evaluateMoves(moveA, moveB);
        //logger.debug("A: {}, B: {}. result: {}", moveA, moveB, result);
        results.result(result);
    }

    /**
     * Evaluates one round of the game
     *
     * @param moveA The move of one player
     * @param moveB The move of another player
     * @return The {@link Result}
     */
    static Result evaluateMoves(final Move moveA, final Move moveB) {
        final Result result;
        if (moveA.beats(moveB)) {
            result = Result.A_WINS;
        } else if (moveB.beats(moveA)) {
            result = Result.B_WINS;
        } else {
            result = Result.TIE;
        }
        return result;
    }
}

public void test() {
    Game.main(null);
}


您的计划成功了!效果很好。

/**
 * A Game of rock-scissor-paper
 *
 * @author ms
 *
 */
public static class Game {

    private static final int NUMBER_OF_GAMES = 100;

    /**
     * An enum encapsulating the results of one move which also holds the strings needed for generating the output
     *
     * @author ms
     *
     */
    enum Result {

        A_WINS("Player A wins "), B_WINS("Player B wins "), TIE("Tie: ");

        private final String output;

        private Result(String output) {
            this.output = output;
        }

        public String getOutput() {
            return output;
        }
    }

    /**
     * @param args
     */
    public static void main(final String[] args) {

        final Player playerA = new PaperPlayer();
        final Player blayerB = new RandomPlayer();

        playGame(playerA, blayerB);
    }

    /**
     * Represents one play of the game.
     */
    private static class Play {

        final Move a;
        final Move b;

        public Play(Move a, Move b) {
            this.a = a;
            this.b = b;
        }

    }

    /**
     * Evaluates one round of the game
     *
     * @param moveA The move of one player
     * @param moveB The move of another player
     * @return The {@link Result}
     */
    static Result evaluateMove(final Move moveA, final Move moveB) {
        final Result result;
        if (moveA.beats(moveB)) {
            result = Result.A_WINS;
        } else if (moveB.beats(moveA)) {
            result = Result.B_WINS;
        } else {
            result = Result.TIE;
        }
        return result;
    }

    /**
     * Plays {@link #NUMBER_OF_GAMES} rounds of the game
     *
     * @param playerA A player
     * @param playerB A player
     * @param results The results are stored here
     */
    private static void playGame(final Player playerA, final Player playerB) {
        Map<Result, Long> results = IntStream.range(0, NUMBER_OF_GAMES)
                // Make one play per round.
                .mapToObj(i -> new Play(playerA.next(), playerB.next()))
                // Evaluate that move to a Result
                .map(p -> evaluateMove(p.a, p.b))
                // Count each result
                .collect(Collectors.groupingBy(r -> r, Collectors.counting()));
        // Print them.
        printResults(results);
    }

    /**
     * Prints out the results.
     *
     * @param results
     */
    private static void printResults(Map<Result, Long> results) {
        StringBuilder s = new StringBuilder();
        for (final Result r : Result.values()) {
            s.append(r.getOutput())
                    .append(results.get(r)).append(" of ")
                    .append(NUMBER_OF_GAMES)
                    .append("\r\n");
        }
        System.out.println(s);
    }

}

public void test() {
    Game.main(null);
}


评论


\ $ \ begingroup \ $
谢谢。尽管恕我直言,Result.result不是一个很好的名字。 Result.toString是Streams的主要候选对象。
\ $ \ endgroup \ $
–马丁·施罗德(MartinSchröder)
2015年5月12日15:40

\ $ \ begingroup \ $
@MartinSchröder-尝试将收集器放入结果中-仍然有点像新手,但是他们渴望练习。
\ $ \ endgroup \ $
–OldCurmudgeon
2015年5月12日15:44

\ $ \ begingroup \ $
更新-更加流化了。
\ $ \ endgroup \ $
–OldCurmudgeon
2015年5月12日15:58

\ $ \ begingroup \ $
直到现在我还没有意识到这一点,但是Lizard和Spock不会在这里玩得开心,因为您不能有重复的键(例如beats.put(SPOCK,SCISSORS); beats.put(SPOCK,岩石);)
\ $ \ endgroup \ $
– h.j.k.
15年5月13日在1:04

\ $ \ begingroup \ $
@ h.j.k。 -好话-我已将其更改为Map >来解决该问题。
\ $ \ endgroup \ $
–OldCurmudgeon
15年5月13日在8:08

#4 楼

您的解决方案不错,但是可以更短一些,并表示为一个函数


从0-2枚举选择(0: rock, 1: paper, 2: scissors)

从0枚举分数列-2 (0: Tie, 1: Player A, 2: Player B)


瞧,该函数用于确定回合点应该到达的位置:

int [] choices = {0,1,2}; // Rock, Paper, Scissors
int [] players = {0,1,2}; // Tie, Player A, Player B
int [] scores = {0,0,0};  // Scores table

int playerAChoice = ...; //get input from player A
int playerBChoice = ...; //get input from player B

//3 choices with wraparound effect
int winner_index = (3 + playerAChoice - playerBChoice) % 3 

scores[winner_index] += 1;


编辑:为什么我选择建议此解决方案而不是枚举。石头剪刀布是平衡游戏类型的子类型,其中每种武器(例如,石头/剪刀/剪刀)与其他所有武器都击败相同数量的武器。例如,可以使用“蜥蜴spock”​​进行扩展。

如果您要使用枚举对此进行建模...这将是一个乏味且容易出错的过程。您必须从字面上复制/粘贴每个游戏的规则,而且它也不是很灵活。

现在,如果要用数学来计算,则这要简单得多。实际上,我为剪刀石头布指定的规则甚至可以进一步推广。任何此类游戏的规则均可按以下方式建模(来源:rospedambo上有关其他武器的维基百科):

Alternatively, the rankings in rock-paper-scissors-Spock-lizard may be modeled 
by a comparison of the parity of the two choices. If it is the same (two odd
numbered moves or two even-numbered ones) then the lower number wins, while if
they are different (one odd and one even) the higher wins. 


枚举在Java中有其位置,但洞察力也是如此您实际上正在解决的问题。

评论


\ $ \ begingroup \ $
不。Java具有枚举,类型比int和array多。这不是C99。
\ $ \ endgroup \ $
–马丁·施罗德(MartinSchröder)
15年5月12日在11:18

\ $ \ begingroup \ $
显然。但是RockPaperScissorsEnterprise并非总是必需的(请参阅:github.com/EnterpriseQualityCoding/FizzBu​​zzEnterpriseEdition)
\ $ \ endgroup \ $
– Arnab Datta
15年5月12日在11:20

\ $ \ begingroup \ $
我同意@MartinSchröder。我们也可以将选择称为啤酒,球员大象和得分,我们可以称之为粉红色。代码仍然有效,但可读性在哪里?
\ $ \ endgroup \ $
– chillworld
15年5月12日在12:21

\ $ \ begingroup \ $
@chillworld我不同意可读性问题。很明显,代码接受了输入选择并评估了获胜者……并相应地更新了分数。让数学表达式计算+1的位置而不是硬编码诸如:Move.ROCK,Move.ROCK,Result.TIE这样的东西真的更糟吗?
\ $ \ endgroup \ $
– Arnab Datta
15年5月12日在12:58



\ $ \ begingroup \ $
Java中的(-1)%3给出-1而不是2。
\ $ \ endgroup \ $
– MarcDefiant
15年5月12日在15:08