Artificial Intelligence, the hero of our time. Etude

Hmm. One of the points regulating the actions of moderators on Habrรฉ is formulated as follows: you should not skip articles that are weakly related to IT-related topics or not at all. What immediately made the author think about whether his post, which tells about some stages of programming his funny and exciting pet-project, a simple AI, which builds a neural network based on the FANN ruby โ€‹โ€‹wrapper for playing crosses, is directly related to the "IT-topic" zeroes? The question does not contain hidden coquetry, because the description of the logic of the program code in my story is far from being of paramount importance. "Yes, this is an evil irony!" - you say. - I do not know.





OK. This development de facto is an illustration of a number of strange observations of the author, a number of acquaintances and even friends-friends of which in recent years ... made him recall the literature lessons of his once when he was a very, very secondary Soviet school. Despite his permanent conviction that it is always possible to "pass" only by something, certain characters of Russian classics are remembered more and more often over time. Or maybe it should be so?





So, right off the bat ... after the first launch, the program begins the self-learning process, playing with itself several tens (a minute - two maximum) of thousands of games (the number, of course, is available for editing in the config; given the not quite usual algorithm described below , underlying the logic of this AI - experiments of this kind can also provide interesting material for inferences). It simulates the learning process inherent in many other Artificial Intelligence, with the only difference that both "players" equally do not know how to play, making absolutely random moves. But the rules of the game apply: if the random move does not match, the program must proceed, and accordingly, the winning side will go to the winning side. Everything is fair: no erasures and hacks, no hidden preferences,no fake doping tests for you, which often overturn the results of sports games in real life.





Next, the game begins with the user: the game protocol logged into a csv file is converted into an array, and the AI โ€‹โ€‹playing with the second number (zeros) solves a philosophical, strangely somewhat very Russian problem, trying to extract from the absurdity and chaos of random moves those that will allow you to win or at least reduce to a draw a game with a living and quite logical thinking opponent.





, ? , : , . , , (, - "") : "" , ... , , .





- , ( ), - . - ... , - , , .





. : , . , , , , , , , : (/). -, Tic Tac Toe - , - ? - , . , , , , - , , ?... , "" ; , " " , - " ", , " !".





, Artificial Intelligence. , 3x3, -, ? , , , , , , , - , ? AI, " " ; - , .





, " ". () , , - . ? , :





        if row[6].to_i - row[3].to_i == 1
            x_data.push([row[0].to_i])
            y_data.push([1]) #   , ..   ,  .
        end
      
      



? - . , .. . ?





, Tic Tac Toe AI with Neural Network ( ). , โ€“ : , โ€“ โ€“ . , , , .





, , - , , . , , , ... ?





. โ€œ"... , ? โ€“ , , : โ€œ , , โ€. , โ€œ โ€ ; , โ€œ โ€. , , ; , . , , , , , , , , : , โ€œ , โ€.





. , , , . , , - , ... , - , , , .





- ( - life style). , :





  WINNING_TRIADS = [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8],
    [0, 3, 6],
    [1, 4, 7],
    [2, 5, 8],
    [6, 4, 2],
    [0, 4, 8]
  ].freeze
      
      



, csv- , :





  def fork?
    WINNING_TRIADS.select do |x|
      @board[x[0]] == @board[x[1]] && @board[x[2]].class != @board[x[0]].class &&
        place_x?(x[0]) ||
        @board[x[1]] == @board[x[2]] && @board[x[0]].class != @board[x[2]].class &&
          place_x?(x[1]) ||
        @board[x[0]] == @board[x[2]] && @board[x[1]].class != @board[x[2]].class &&
          place_x?(x[0])
    end
  end
      
      



, ...





  if @game.fork?.size > 1
      
      



... .





, . : , , , . , .





:





  DANGEROUS_SITUATIONS_1 = [
    [6, 4, 2],
    [0, 4, 8]
  ].freeze

  DANGEROUS_SITUATIONS_2 = [
    [0, 4, 7],
    [0, 4, 5],
    [2, 4, 3],
    [2, 4, 7],
    [3, 4, 8],
    [1, 4, 8],
    [1, 4, 6],
    [5, 4, 6]
  ].freeze
      
      



  def fork_danger_1?
    DANGEROUS_SITUATIONS_1.detect do |x|
      @board[x[0]] == @board[x[2]] &&
        @board[x[0]] != @board[x[1]]
    end
  end
  
  def fork_danger_2?
    DANGEROUS_SITUATIONS_2.detect do |x|
      @board[x[0]] == @board[x[2]] &&
        @board[x[0]] != @board[x[1]]
    end
  end

  def fork_danger_3?
    DANGEROUS_SITUATIONS_1.detect do |x|
      @board[x[0]] != @board[x[2]] &&
        @board[x[1]] == @board[x[2]]
    end
  end
      
      



, , , , , AI : 1. , 2. 3. (.. , , , ). , , . , Neural Network.





  array_of_games.each do |row|
      row.each do |e|
        next unless e == current_position

        if row[6].to_i - row[3].to_i == 2 && row[4] == 'O' && row[2].to_f != 0.2
          unacceptable_moves_array << row[0]
        # Find moves that inevitably lead to a fork:
        elsif fork_danger_1 && row[3].to_i == 3 && row[0].to_i.odd?
          unacceptable_moves_array << row[0]
        elsif (fork_danger_2 || fork_danger_3) && row[3].to_i == 3 && row[0].to_i.even?
          unacceptable_moves_array << row[0]
        end
        next if row[5].nil?

        # Find moves that may lead to a fork:
        array_of_moves_to_fork << row[0] if row[3].to_i == row[5].to_i
        # Find attacking moves:
        attack_moves_array << row[0] if row[3].to_i == row[5].to_i && row[6].to_i < 7
      end
    end
      
      



, , , AI , . ... , , , , ... , "" . , , - , , ... ? - ", , ", .





 array_of_games.each do |row|
      row.each do |e|
        next unless e == current_position

        next if arrays[0].include?(row[0])

        unless arrays[1].include?(row[0]) && !arrays[2].include?(row[0])
          if row[6].to_i - row[3].to_i == 1
            x_data.push([row[0].to_i])
            y_data.push([1])
          elsif row[6].to_i - row[3].to_i == 3
            if arrays[2].include?(row[0])
              x_data.push([row[0].to_i])
              y_data.push([0.9])
            elsif arrays[1].include?(row[0])
              x_data.push([row[0].to_i])
              y_data.push([0.3])
            end
          else
            x_data.push([row[0].to_i])
            y_data.push([row[2].to_f])
          end
        end
      end
      
      



:





    data = nn_data(board, fork_danger_1, fork_danger_2, fork_danger_3, array_of_games)
    fann_results_array = []
      train = RubyFann::TrainData.new(inputs: data[0], desired_outputs: data[1])
      model = RubyFann::Standard.new(
        num_inputs: 1,
        hidden_neurons: [4],
        num_outputs: 1
      )
      model.train_on_data(train, 5000, 500, 0.01)
      data[0].flatten.each do |i|
        fann_results_array << model.run([i])
      end
    result = data[0][fann_results_array.index(fann_results_array.max)]
      
      



: ( csv-) Neural Network .





- , - , . csv- ( , ), . , - - , , .





PS The described code is always available in full (and not fragmentarily, as the format of the article dictates) in my github , of course, anyone can make a git clone and experiment with the code, or just play. I am not a supporter of running ruby-application under Windows, this is not a very good idea, but in this case it will work, I tried it. Perhaps it will turn out a little less impressive than in the Linux console, but the logic will work.








All Articles