#! /usr/bin/env python3 # def truel_simulation ( p, strategy, truel_num ): #*****************************************************************************80 # ## truel_simulation() simulates a truel (a 3-way duel). # # Discussion: # # Three players fight a three-sided duel, or "truel". # # Player 1 chooses a target (player 2, player 3, or nobody), and fires. # If aimed at a player, he hits with a probability of P(1). # # Then it is Player 2's turn, and Player 3's turn. # # Play continues to alternate until there is only one survivor. # # Strategy 1: # # Each player uses their turn to fire at the opponent with the # highest accuracy. # # Strategy 4: # # * The player with the highest accuracy will always aim at the player # with the next highest accuracy. # * The player with the medium accuracy will always aim at the player # with the highest accuracy if that player is dead, then the player # will aim at the player with lowest accuracy. # * The player with lowest accuracy will aim at NO ONE, as long as # the other two players are both alive. Once there is only one # opponent, he will aim at that opponent. # # Although the program was written to simulate a 3-way duel, it can # handle duels with an arbitrary number of players. It determines the number # of players from the length of the P array. Therefore, to play a duel with # 4 players, simply supply 4 numbers in P. # # Licensing: # # This code is distributed under the MIT license. # # Modified: # # 06 July 2022 # # Author: # # John Burkardt # # Reference: # # Martin Gardner, # "The Triangular Duel", # The Second Scientific American Book of Mathematical # Puzzles and Diversions, # Simon and Schuster, 1961. # # Marc Kilgour, Steven Brams, # The Truel, # Mathematics Magazine, # Volume 70, Number 5, December 1997, pages 315-326. # # Paul Nahin, # Duelling Idiots and Other Probability Puzzlers, # Princeton University Press, 2000, # ISBN13: 978-0691009797, # LC: QA273.N29. # # Martin Shubik, # "Does the Fittest Necessarily Survive?", # in Readings in Game Theory and Political Behavior, # edited by Martin Shubik, # Doubleday, 1954, # LC: H61.S53. # # Input: # # real P(*), the probabilities that player I will hit an opponent # in a single shot. Each P(I) should be between 0 and 1. # # integer STRATEGY, the index of the strategy. # 1: each player fires at best opponent. # 4: player 1 at 2, player 2 at 1, player 3 withholds. # # integer TRUEL_NUM, the number of truels to run. # # Output: # # real S(*), the estimated probablity that player I will survive. # # real TURN_AVERAGE, the average number of turns required to # complete the truel. # import numpy as np print ( '' ) print ( 'truel_simulation():' ) player_num = len ( p ) s = np.zeros ( player_num ) turn_average = 0 for duel in range ( 0, truel_num ): if ( strategy == 1 ): survivor, turn_num = truel_strategy1 ( p ) elif ( strategy == 4 ): survivor, turn_num = truel_strategy4 ( p ) s[survivor] = s[survivor] + 1 turn_average = turn_average + turn_num s = s / truel_num turn_average = turn_average / truel_num return s, turn_average def truel_strategy1 ( p ): #*****************************************************************************80 # ## truel_strategy1() returns the outcome of a truel with strategy 1. # # Discussion # # Strategy 1: # # * Each player chooses the opponent with highest accuracy as target. # # Licensing: # # This code is distributed under the MIT license. # # Modified: # # 06 July 2022 # # Author: # # John Burkardt # # Reference: # # Martin Shubik, # "Does the Fittest Necessarily Survive?", # in Readings in Game Theory and Political Behavior, # edited by Martin Shubik, # Doubleday, 1954, # LC: H61.S53. # # Input: # # real P(*), the probabilities that player I will hit an opponent # in a single shot. Each P(I) should be between 0 and 1. # # Output: # # integer SURVIVOR, the survivor. # import numpy as np player_num = len ( p ) alive_num = player_num q = p.copy() turn_num = 0 while ( True ): for i in range ( 0, player_num ): # # Use the Q vector to keep track of who's alive, and who's worth # shooting at. To avoid shooting yourself, temporarily zero your # own P value. # if ( 0.0 < q[i] ): turn_num = turn_num + 1 q_save = q[i] q[i] = 0.0 q_max = np.max ( q ) target = np.argmax ( q ) r = np.random.random ( ) if ( r <= q_save ): q[target] = 0.0 alive_num = alive_num - 1 if ( alive_num == 1 ): survivor = i return survivor, turn_num q[i] = q_save return survivor, turn_num def truel_strategy4 ( p ): #*****************************************************************************80 # ## truel_strategy4() returns the outcome of a truel with strategy 4. # # Discussion # # Strategy 4: # # * The player with the highest accuracy will always aim at the player # with the next highest accuracy. # * The player with the medium accuracy will always aim at the player # with the highest accuracy if that player is dead, then the player # will aim at the player with lowest accuracy. # * The player with lowest accuracy will aim at NO ONE, as long as # the other two players are both alive. Once there is only one # opponent, he will aim at that opponent. # # With the probabilities ( 2/6, 4/6, 5/6), the weakest player has about # a 70% chance of surviving, using this strategy. # # With the probabilities ( 5/6, 4/6, 2/6), the weakest player has about # a 40% chance of surviving. # # Licensing: # # This code is distributed under the MIT license. # # Modified: # # 06 July 2022 # # Author: # # John Burkardt # # Reference: # # Martin Shubik, # "Does the Fittest Necessarily Survive?", # in Readings in Game Theory and Political Behavior, # edited by Martin Shubik, # Doubleday, 1954, # LC: H61.S53. # # Input: # # real P(*), the probabilities that player I will hit an opponent # in a single shot. Each P(I) should be between 0 and 1. # # Output: # # integer SURVIVOR, the survivor. # import numpy as np player_num = len ( p ) alive_num = player_num p_min = np.min ( p ) p_max = np.max ( p ) # # Make a copy of P that we can manipulate. # q = p.copy() turn_num = 0 while ( True ): for i in range ( 0, player_num ): # # Use the Q vector to keep track of who's alive, and who's worth # shooting at. To avoid shooting yourself, temporarily zero your # own P value. # if ( 0.0 < q[i] ): turn_num = turn_num + 1 # # If there's more than one other player, # and you're the worst player, # and you're strictly a worst player, # then don't do anything. # if ( 2 < alive_num and q[i] == p_min and p_min < p_max ): continue # # Otherwise, aim at the target (highest opponent) and # hit them with your accuracy. # else: q_save = q[i] q[i] = 0.0 q_target = np.max ( q ) target = np.argmax ( q ) r = np.random.random ( ) if ( r <= q_save ): q[target] = 0.0 alive_num = alive_num - 1 if ( alive_num == 1 ): survivor = i return survivor, turn_num q[i] = q_save return survivor, turn_num def truel_simulation_test ( ): #*****************************************************************************80 # ## truel_simulation_test() tests truel_simulation(). # # Licensing: # # This code is distributed under the MIT license. # # Modified: # # 06 July 2022 # # Author: # # John Burkardt # import numpy as np import platform print ( '' ) print ( 'truel_simulation_test():' ) print ( ' Python version: ' + platform.python_version ( ) ) print ( ' Test truel_simulation().' ) p = np.array ( [ 2/6, 4/6, 5/6 ] ) strategy = 1 truel_num = 1000 s, turn_average = truel_simulation ( p, strategy, truel_num ) print ( '' ) print ( ' Truel statistics estimated from', truel_num, 'simulations' ) print ( ' Strategy = ', strategy ) print ( '' ) print ( ' Player Accuracy Survival' ) print ( '' ) for i in range ( 0, 3 ): print ( ' %d %8.2f %8.2f'% ( i, p[i], s[i] ) ) p = np.array ( [ 2/6, 4/6, 5/6 ] ) strategy = 4 truel_num = 1000 s, turn_average = truel_simulation ( p, strategy, truel_num ) print ( '' ) print ( ' Truel statistics estimated from', truel_num, 'simulations' ) print ( ' Strategy = ', strategy ) print ( '' ) print ( ' Player Accuracy Survival' ) print ( '' ) for i in range ( 0, 3 ): print ( ' %d %8.2f %8.2f'% ( i, p[i], s[i] ) ) # # Terminate. # print ( '' ) print ( 'truel_simulation_test():' ) print ( ' Normal end of execution.' ) return def timestamp ( ): #*****************************************************************************80 # ## timestamp() prints the date as a timestamp. # # Licensing: # # This code is distributed under the MIT license. # # Modified: # # 21 August 2019 # # Author: # # John Burkardt # import time t = time.time ( ) print ( time.ctime ( t ) ) return if ( __name__ == '__main__' ): timestamp ( ) truel_simulation_test ( ) timestamp ( )