📜 ⬆️ ⬇️

Game for programmers - Bulls and cows

Hi habraedi. I will tell you how to write a Python engine and an approximate bot of the game for programmers. Games for programmers are games that people do not play, but programs.


We will write a version of the game "Bulls and Cows" , in English which is called "Bulls and Cows". The rules of the original game between two people can be read here .
The rules of our game:
  1. The engine conceives a number, it is n-digit and the numbers in it cannot be repeated. The engine tells the player program the number n
  2. The player program assumes a certain number, informs its engine
  3. The engine tells the player program how many digits from the player program number are in the intended and stand in place (Bulls), and how many are in the planned, but do not stand in their place (cows).
  4. If the number is not guessed, return to step 2


The player program will communicate with the engine via stdin / out. Our specification assumes that the engine runs the player programs passed to it as arguments.
')

Engine



We first write the functional parts:

Copy Source | Copy HTML #!/usr/bin/python import random class Game (object): def __init__ (self, number= 6 ): if number > 10 : exit() self .number = number self .secret = self . make_secret ( self .number) self .main() def is_right_secret (self, secret): """check all digits are different""" dct = {} for digit in secret: try : if dct[digit]: return False except KeyError: dct[digit] = True return True def make_secret (self, number): """generate number-counted secret""" while True: result = "" for i in range (number): result += "%d" % random .randint( 0 , 9 ) if self . is_right_secret (result): return result
  1. Copy Source | Copy HTML #!/usr/bin/python import random class Game (object): def __init__ (self, number= 6 ): if number > 10 : exit() self .number = number self .secret = self . make_secret ( self .number) self .main() def is_right_secret (self, secret): """check all digits are different""" dct = {} for digit in secret: try : if dct[digit]: return False except KeyError: dct[digit] = True return True def make_secret (self, number): """generate number-counted secret""" while True: result = "" for i in range (number): result += "%d" % random .randint( 0 , 9 ) if self . is_right_secret (result): return result
  2. Copy Source | Copy HTML #!/usr/bin/python import random class Game (object): def __init__ (self, number= 6 ): if number > 10 : exit() self .number = number self .secret = self . make_secret ( self .number) self .main() def is_right_secret (self, secret): """check all digits are different""" dct = {} for digit in secret: try : if dct[digit]: return False except KeyError: dct[digit] = True return True def make_secret (self, number): """generate number-counted secret""" while True: result = "" for i in range (number): result += "%d" % random .randint( 0 , 9 ) if self . is_right_secret (result): return result
  3. Copy Source | Copy HTML #!/usr/bin/python import random class Game (object): def __init__ (self, number= 6 ): if number > 10 : exit() self .number = number self .secret = self . make_secret ( self .number) self .main() def is_right_secret (self, secret): """check all digits are different""" dct = {} for digit in secret: try : if dct[digit]: return False except KeyError: dct[digit] = True return True def make_secret (self, number): """generate number-counted secret""" while True: result = "" for i in range (number): result += "%d" % random .randint( 0 , 9 ) if self . is_right_secret (result): return result
  4. Copy Source | Copy HTML #!/usr/bin/python import random class Game (object): def __init__ (self, number= 6 ): if number > 10 : exit() self .number = number self .secret = self . make_secret ( self .number) self .main() def is_right_secret (self, secret): """check all digits are different""" dct = {} for digit in secret: try : if dct[digit]: return False except KeyError: dct[digit] = True return True def make_secret (self, number): """generate number-counted secret""" while True: result = "" for i in range (number): result += "%d" % random .randint( 0 , 9 ) if self . is_right_secret (result): return result
  5. Copy Source | Copy HTML #!/usr/bin/python import random class Game (object): def __init__ (self, number= 6 ): if number > 10 : exit() self .number = number self .secret = self . make_secret ( self .number) self .main() def is_right_secret (self, secret): """check all digits are different""" dct = {} for digit in secret: try : if dct[digit]: return False except KeyError: dct[digit] = True return True def make_secret (self, number): """generate number-counted secret""" while True: result = "" for i in range (number): result += "%d" % random .randint( 0 , 9 ) if self . is_right_secret (result): return result
  6. Copy Source | Copy HTML #!/usr/bin/python import random class Game (object): def __init__ (self, number= 6 ): if number > 10 : exit() self .number = number self .secret = self . make_secret ( self .number) self .main() def is_right_secret (self, secret): """check all digits are different""" dct = {} for digit in secret: try : if dct[digit]: return False except KeyError: dct[digit] = True return True def make_secret (self, number): """generate number-counted secret""" while True: result = "" for i in range (number): result += "%d" % random .randint( 0 , 9 ) if self . is_right_secret (result): return result
  7. Copy Source | Copy HTML #!/usr/bin/python import random class Game (object): def __init__ (self, number= 6 ): if number > 10 : exit() self .number = number self .secret = self . make_secret ( self .number) self .main() def is_right_secret (self, secret): """check all digits are different""" dct = {} for digit in secret: try : if dct[digit]: return False except KeyError: dct[digit] = True return True def make_secret (self, number): """generate number-counted secret""" while True: result = "" for i in range (number): result += "%d" % random .randint( 0 , 9 ) if self . is_right_secret (result): return result
  8. Copy Source | Copy HTML #!/usr/bin/python import random class Game (object): def __init__ (self, number= 6 ): if number > 10 : exit() self .number = number self .secret = self . make_secret ( self .number) self .main() def is_right_secret (self, secret): """check all digits are different""" dct = {} for digit in secret: try : if dct[digit]: return False except KeyError: dct[digit] = True return True def make_secret (self, number): """generate number-counted secret""" while True: result = "" for i in range (number): result += "%d" % random .randint( 0 , 9 ) if self . is_right_secret (result): return result
  9. Copy Source | Copy HTML #!/usr/bin/python import random class Game (object): def __init__ (self, number= 6 ): if number > 10 : exit() self .number = number self .secret = self . make_secret ( self .number) self .main() def is_right_secret (self, secret): """check all digits are different""" dct = {} for digit in secret: try : if dct[digit]: return False except KeyError: dct[digit] = True return True def make_secret (self, number): """generate number-counted secret""" while True: result = "" for i in range (number): result += "%d" % random .randint( 0 , 9 ) if self . is_right_secret (result): return result
  10. Copy Source | Copy HTML #!/usr/bin/python import random class Game (object): def __init__ (self, number= 6 ): if number > 10 : exit() self .number = number self .secret = self . make_secret ( self .number) self .main() def is_right_secret (self, secret): """check all digits are different""" dct = {} for digit in secret: try : if dct[digit]: return False except KeyError: dct[digit] = True return True def make_secret (self, number): """generate number-counted secret""" while True: result = "" for i in range (number): result += "%d" % random .randint( 0 , 9 ) if self . is_right_secret (result): return result
  11. Copy Source | Copy HTML #!/usr/bin/python import random class Game (object): def __init__ (self, number= 6 ): if number > 10 : exit() self .number = number self .secret = self . make_secret ( self .number) self .main() def is_right_secret (self, secret): """check all digits are different""" dct = {} for digit in secret: try : if dct[digit]: return False except KeyError: dct[digit] = True return True def make_secret (self, number): """generate number-counted secret""" while True: result = "" for i in range (number): result += "%d" % random .randint( 0 , 9 ) if self . is_right_secret (result): return result
  12. Copy Source | Copy HTML #!/usr/bin/python import random class Game (object): def __init__ (self, number= 6 ): if number > 10 : exit() self .number = number self .secret = self . make_secret ( self .number) self .main() def is_right_secret (self, secret): """check all digits are different""" dct = {} for digit in secret: try : if dct[digit]: return False except KeyError: dct[digit] = True return True def make_secret (self, number): """generate number-counted secret""" while True: result = "" for i in range (number): result += "%d" % random .randint( 0 , 9 ) if self . is_right_secret (result): return result
  13. Copy Source | Copy HTML #!/usr/bin/python import random class Game (object): def __init__ (self, number= 6 ): if number > 10 : exit() self .number = number self .secret = self . make_secret ( self .number) self .main() def is_right_secret (self, secret): """check all digits are different""" dct = {} for digit in secret: try : if dct[digit]: return False except KeyError: dct[digit] = True return True def make_secret (self, number): """generate number-counted secret""" while True: result = "" for i in range (number): result += "%d" % random .randint( 0 , 9 ) if self . is_right_secret (result): return result
  14. Copy Source | Copy HTML #!/usr/bin/python import random class Game (object): def __init__ (self, number= 6 ): if number > 10 : exit() self .number = number self .secret = self . make_secret ( self .number) self .main() def is_right_secret (self, secret): """check all digits are different""" dct = {} for digit in secret: try : if dct[digit]: return False except KeyError: dct[digit] = True return True def make_secret (self, number): """generate number-counted secret""" while True: result = "" for i in range (number): result += "%d" % random .randint( 0 , 9 ) if self . is_right_secret (result): return result
  15. Copy Source | Copy HTML #!/usr/bin/python import random class Game (object): def __init__ (self, number= 6 ): if number > 10 : exit() self .number = number self .secret = self . make_secret ( self .number) self .main() def is_right_secret (self, secret): """check all digits are different""" dct = {} for digit in secret: try : if dct[digit]: return False except KeyError: dct[digit] = True return True def make_secret (self, number): """generate number-counted secret""" while True: result = "" for i in range (number): result += "%d" % random .randint( 0 , 9 ) if self . is_right_secret (result): return result
  16. Copy Source | Copy HTML #!/usr/bin/python import random class Game (object): def __init__ (self, number= 6 ): if number > 10 : exit() self .number = number self .secret = self . make_secret ( self .number) self .main() def is_right_secret (self, secret): """check all digits are different""" dct = {} for digit in secret: try : if dct[digit]: return False except KeyError: dct[digit] = True return True def make_secret (self, number): """generate number-counted secret""" while True: result = "" for i in range (number): result += "%d" % random .randint( 0 , 9 ) if self . is_right_secret (result): return result
  17. Copy Source | Copy HTML #!/usr/bin/python import random class Game (object): def __init__ (self, number= 6 ): if number > 10 : exit() self .number = number self .secret = self . make_secret ( self .number) self .main() def is_right_secret (self, secret): """check all digits are different""" dct = {} for digit in secret: try : if dct[digit]: return False except KeyError: dct[digit] = True return True def make_secret (self, number): """generate number-counted secret""" while True: result = "" for i in range (number): result += "%d" % random .randint( 0 , 9 ) if self . is_right_secret (result): return result
  18. Copy Source | Copy HTML #!/usr/bin/python import random class Game (object): def __init__ (self, number= 6 ): if number > 10 : exit() self .number = number self .secret = self . make_secret ( self .number) self .main() def is_right_secret (self, secret): """check all digits are different""" dct = {} for digit in secret: try : if dct[digit]: return False except KeyError: dct[digit] = True return True def make_secret (self, number): """generate number-counted secret""" while True: result = "" for i in range (number): result += "%d" % random .randint( 0 , 9 ) if self . is_right_secret (result): return result
  19. Copy Source | Copy HTML #!/usr/bin/python import random class Game (object): def __init__ (self, number= 6 ): if number > 10 : exit() self .number = number self .secret = self . make_secret ( self .number) self .main() def is_right_secret (self, secret): """check all digits are different""" dct = {} for digit in secret: try : if dct[digit]: return False except KeyError: dct[digit] = True return True def make_secret (self, number): """generate number-counted secret""" while True: result = "" for i in range (number): result += "%d" % random .randint( 0 , 9 ) if self . is_right_secret (result): return result
  20. Copy Source | Copy HTML #!/usr/bin/python import random class Game (object): def __init__ (self, number= 6 ): if number > 10 : exit() self .number = number self .secret = self . make_secret ( self .number) self .main() def is_right_secret (self, secret): """check all digits are different""" dct = {} for digit in secret: try : if dct[digit]: return False except KeyError: dct[digit] = True return True def make_secret (self, number): """generate number-counted secret""" while True: result = "" for i in range (number): result += "%d" % random .randint( 0 , 9 ) if self . is_right_secret (result): return result
  21. Copy Source | Copy HTML #!/usr/bin/python import random class Game (object): def __init__ (self, number= 6 ): if number > 10 : exit() self .number = number self .secret = self . make_secret ( self .number) self .main() def is_right_secret (self, secret): """check all digits are different""" dct = {} for digit in secret: try : if dct[digit]: return False except KeyError: dct[digit] = True return True def make_secret (self, number): """generate number-counted secret""" while True: result = "" for i in range (number): result += "%d" % random .randint( 0 , 9 ) if self . is_right_secret (result): return result
  22. Copy Source | Copy HTML #!/usr/bin/python import random class Game (object): def __init__ (self, number= 6 ): if number > 10 : exit() self .number = number self .secret = self . make_secret ( self .number) self .main() def is_right_secret (self, secret): """check all digits are different""" dct = {} for digit in secret: try : if dct[digit]: return False except KeyError: dct[digit] = True return True def make_secret (self, number): """generate number-counted secret""" while True: result = "" for i in range (number): result += "%d" % random .randint( 0 , 9 ) if self . is_right_secret (result): return result
  23. Copy Source | Copy HTML #!/usr/bin/python import random class Game (object): def __init__ (self, number= 6 ): if number > 10 : exit() self .number = number self .secret = self . make_secret ( self .number) self .main() def is_right_secret (self, secret): """check all digits are different""" dct = {} for digit in secret: try : if dct[digit]: return False except KeyError: dct[digit] = True return True def make_secret (self, number): """generate number-counted secret""" while True: result = "" for i in range (number): result += "%d" % random .randint( 0 , 9 ) if self . is_right_secret (result): return result
  24. Copy Source | Copy HTML #!/usr/bin/python import random class Game (object): def __init__ (self, number= 6 ): if number > 10 : exit() self .number = number self .secret = self . make_secret ( self .number) self .main() def is_right_secret (self, secret): """check all digits are different""" dct = {} for digit in secret: try : if dct[digit]: return False except KeyError: dct[digit] = True return True def make_secret (self, number): """generate number-counted secret""" while True: result = "" for i in range (number): result += "%d" % random .randint( 0 , 9 ) if self . is_right_secret (result): return result
  25. Copy Source | Copy HTML #!/usr/bin/python import random class Game (object): def __init__ (self, number= 6 ): if number > 10 : exit() self .number = number self .secret = self . make_secret ( self .number) self .main() def is_right_secret (self, secret): """check all digits are different""" dct = {} for digit in secret: try : if dct[digit]: return False except KeyError: dct[digit] = True return True def make_secret (self, number): """generate number-counted secret""" while True: result = "" for i in range (number): result += "%d" % random .randint( 0 , 9 ) if self . is_right_secret (result): return result
  26. Copy Source | Copy HTML #!/usr/bin/python import random class Game (object): def __init__ (self, number= 6 ): if number > 10 : exit() self .number = number self .secret = self . make_secret ( self .number) self .main() def is_right_secret (self, secret): """check all digits are different""" dct = {} for digit in secret: try : if dct[digit]: return False except KeyError: dct[digit] = True return True def make_secret (self, number): """generate number-counted secret""" while True: result = "" for i in range (number): result += "%d" % random .randint( 0 , 9 ) if self . is_right_secret (result): return result
  27. Copy Source | Copy HTML #!/usr/bin/python import random class Game (object): def __init__ (self, number= 6 ): if number > 10 : exit() self .number = number self .secret = self . make_secret ( self .number) self .main() def is_right_secret (self, secret): """check all digits are different""" dct = {} for digit in secret: try : if dct[digit]: return False except KeyError: dct[digit] = True return True def make_secret (self, number): """generate number-counted secret""" while True: result = "" for i in range (number): result += "%d" % random .randint( 0 , 9 ) if self . is_right_secret (result): return result


Here we are thinking about the number that player programs will guess. We also check that all the numbers in the number were different. then we run Game.main - the main program loop.

It is also necessary to add the counting function of bulls and cows:

Copy Source | Copy HTML
  1. def count_bulls_cows (self, number, secret):
  2. "" "count bulls and cows in number / secret" ""
  3. cows, bulls = 0 , 0
  4. dct = {}
  5. for i in range ( len (secret)):
  6. if secret [i] == number [i]:
  7. bulls + = 1
  8. else :
  9. dct [secret [i]] = True
  10. try :
  11. if dct [number [i]]:
  12. cows + = 1
  13. except KeyError:
  14. pass
  15. return bulls, cows


Now we introduce constants that will denote code words for communicating with programs by players, and some others:

Copy Source | Copy HTML
  1. MESS_NUMBER_DIGITS = "number_digits" #start message
  2. MESS_GUESS = "guess!"
  3. MESS_COWS = "cows:" #If the matching digits on different positions, they are "cows"
  4. MESS_BULLS = "bulls:" #If the matching digits are on their right positions, they are "bulls"
  5. MESS_BULLS_COWS_SPLIT = "|" #Splitter between cows and bulls message
  6. MAX_TURN_NUMBER = 1000


Now we will describe the gameplay in main (): (I bring it with debug messages)

Copy Source | Copy HTML
  1. def main (self):
  2. for player in self .players:
  3. player.connect ()
  4. player.send ( "% s% d" % (MESS_NUMBER_DIGITS, self .number))
  5. print ( "% s% d" % (MESS_NUMBER_DIGITS, self .number))
  6. starttime = time . time ()
  7. while true:
  8. if player.turns> MAX_TURN_NUMBER:
  9. print ">>>> Player '% s' turns timeout" % player.name
  10. player.disconnect ()
  11. break
  12. answer = player.recieve ()
  13. print ">>>> Answer is% s" % answer
  14. if len ( self .secret)! = len (answer):
  15. self .stop_game ( "wrong number" )
  16. bulls, cows = self .count_bulls_cows (answer, self .secret)
  17. if bulls == self .number:
  18. player.time = time . time () - starttime
  19. player.send ( "% s" % MESS_GUESS)
  20. player.disconnect ()
  21. print ">>>> Player disconected"
  22. break
  23. else :
  24. player.turns + = 1
  25. player.send ( "% s% d% s% s% d" % (MESS_BULLS, bulls, \
  26. MESS_BULLS_COWS_SPLIT, MESS_COWS, cows))
  27. print ( ">>>>% s% d% s% s% d" % (MESS_BULLS, bulls, \
  28. MESS_BULLS_COWS_SPLIT, MESS_COWS, cows))
  29. winner = self .players [ 0 ]
  30. for player in self .players:
  31. if player.turns <winner.turns:
  32. winner = player
  33. print ">>>> Winner name:% s" % winner.name


It's all clear. Take turns playing with each player. First, send the number of digits in the hidden number. We notice time. We take the player's guess number, if you have not guessed (the number of bulls is not equal to the number of digits in the number) - we send the number of bulls / cows and increase the number of moves required by the player to guess, if we guess - stop the timer, write him what he won, disconnect from the player , go to the next.

At the end choose the winner.

We describe the connection technology with the processes of player programs in the Player class:

Copy Source | Copy HTML
  1. class Player (object):
  2. def __init__ (self, name, progname):
  3. self .name = name
  4. self .turns = 0
  5. self .time = - 1
  6. self .progname = progname
  7. def connect (self):
  8. self .__ proc = subprocess .Popen ( self .progname, stdin = subprocess .PIPE, \
  9. stdout = subprocess .PIPE)
  10. def send (self, sendstring):
  11. self .__ proc.stdin.write (sendstring + '\ n' )
  12. def recieve (self)
  13. return self .__ proc.stdout. readline () .strip ( '\ n' )
  14. def disconnect (self)
  15. self .__ proc.terminate ()


Let's rewrite Game .__ init__ a bit to initialize players:

Copy Source | Copy HTML
  1. def __init__ (self, number, players_progs):
  2. if number> 10 :
  3. exit ()
  4. self .number = number
  5. self .secret = self .make_secret ( self .number)
  6. self .players = []
  7. for playername in players_progs:
  8. self .players.append (Player (playername, players_progs [playername]))
  9. self .main ()


Dumb bot



Now you need to test the result. For tests, we need a player program, the simplest. Then it will be an api - class from which real player programs will be inherited
Create a subfolder api in the project folder, and the file mooapi.py in it:

Copy Source | Copy HTML
  1. #! / usr / bin / python
  2. import random
  3. import sys
  4. MESS_NUMBER_DIGITS = "number_digits" #start message
  5. MESS_GUESS = "guess!"
  6. MESS_COWS = "cows:" #If the matching digits on different positions, they are "cows"
  7. MESS_BULLS = "bulls:" #If the matching digits are on their right positions, they are "bulls"
  8. MESS_BULLS_COWS_SPLIT = "|" #Splitter between cows and bulls message
  9. class MooPlayer (object):
  10. ## technical functions
  11. def send (self, st):
  12. sys .stdout.write ( "% s \ n" % st)
  13. sys .stdout.flush ()
  14. def recieve (self):
  15. return sys .stdin. readline () .strip ( "\ n" )
  16. def number_split (self, st):
  17. num = st.split ( '' )
  18. if num [ 0 ] == MESS_NUMBER_DIGITS:
  19. return int (num [ 1 ])
  20. else :
  21. exit ()
  22. def answer_split (self, st):
  23. try :
  24. bullst, cowst = st.split (MESS_BULLS_COWS_SPLIT)
  25. except ValueError:
  26. exit ()
  27. bulls, cows = bullst.split ( '' ), cowst.split ( '' )
  28. if bulls [ 0 ] == MESS_BULLS and cows [ 0 ] == MESS_COWS:
  29. return bulls [ 1 ], cows [ 1 ]
  30. else :
  31. exit ()
  32. def main (self):
  33. self .number = self . number_split ( self . recieve ())
  34. while true:
  35. try :
  36. self .oldguess = guess
  37. guess = self . guess (cows = cows, bulls = bulls, firstrun = False)
  38. except NameError:
  39. guess = self . guess (firstrun = true)
  40. self . send ( guess )
  41. answer = self . recieve ()
  42. if answer == MESS_GUESS:
  43. exit ()
  44. else :
  45. bulls, cows = self . answer_split (answer)
  46. ## some functions
  47. def is_right_secret (self, secret):
  48. "" "check all digits are different" ""
  49. dct = {}
  50. for digit in secret:
  51. try :
  52. if dct [digit]:
  53. return false
  54. except KeyError:
  55. dct [digit] = True
  56. return true
  57. def generate_number (self, number):
  58. "" "generate number-counted secret" ""
  59. while true:
  60. result = ""
  61. for i in range (number):
  62. result + = "% d" % random .randint ( 0 , 9 )
  63. if self . is_right_secret (result):
  64. return result
  65. ############ Main guess function ###########
  66. def guess (self, firstrun, bulls = 0 , cows = 0 ):
  67. "This is the main api function. It means that it is the first time run. <br/> bulls / cows number. <br/> also you have: <br/> self.number - number of digits <br/> self.oldguess - old guess try <br/> "" "
  68. return self . generate_number ( self .number)
  69. if __name__ == "__main__" :
  70. mooplayer = mooplayer ()
  71. mooplayer. main ()


Everything is simple, a part of the code is copied from the engine module, no analysis takes place, it is simply guessed by a random number.
The one who will write the player program will need to import the mooapi module, inherit its class from MooPlayer and override the function MooPlayer.guess ()

Why I haven’t imported the engine module - for clarity, to those who will write api for other programming languages.
Now you can run the player program and try to play bulls and cows with it:

>>number_digits 4
5032
>>bulls: 0|cows: 0
4123
>>bulls: 0|cows: 0
2890
>>bulls: 0|cows: 0
2691
>>guess!


(>>) indicates keyboard input

Run



now add to the end of the engine module:

Copy Source | Copy HTML
  1. if __name__ == "__main__" :
  2. game = Game ( 2 , { 'Player1' : "api / mooapi.py" , 'Player2' : "api / mooapi.py" , 'HabraMan' : "api / mooapi.py" })


This will start the game with two-digit numbers, three players with the same api / mooapi.py programs. With double digits, so that our stupid program can guess.

Run the engine module:
>>>> bulls: 0|cows: 1
>>>> Answer is 67
>>>> bulls:1 cows:0
>>>> bulls: 1|cows: 0
>>>> Answer is 87
>>>> bulls:0 cows:0
>>>> bulls: 0|cows: 0
>>>> Answer is 49
>>>> bulls:0 cows:0
>>>> bulls: 0|cows: 0
>>>> Answer is 54
>>>> bulls:0 cows:0
>>>> bulls: 0|cows: 0
>>>> Answer is 82
>>>> bulls:1 cows:0
>>>> bulls: 1|cows: 0
>>>> Answer is 21
>>>> bulls:0 cows:0
>>>> bulls: 0|cows: 0
>>>> Answer is 62
>>>> bulls:2 cows:0
>>>> Player disconected
>>>> Winner name: HabraMan


Works!
In the next post I will tell you how to make the program useful - to screw in it the parsing of the command line arguments and outputting all the good in xml, in which the input to our future flash player on our future site is missing .

Link to full texts of programs

The joy of the engine is that it can be screwed to a multi-site that we are writing now.

You can be the first to implement the Honorable Knuth's algorithm in our bot.

Source: https://habr.com/ru/post/74727/


All Articles