How to represent repeating patterns in data - artificial-intelligence

I've a homework assignment that uses MCTS (http://mcts.ai/code/python.html) to play as many games of tic tac toe as required using MCTS. The goal of the assignment is to train a decision tree classifier that can predict what the best action is to take depending on the current state of the game and the player playing the game. The data marks a 1.0 or 2.0 or 0 depending on which player has marked his chosen position in the tic tac toe grid (0 for no players). Ive so far managed to save to CSV the data in the format like this:
Unnamed: 0 player 0 1 2 ... 6 7 8 best_move won
0 0 1.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 4 0
My first and main question is how can i make a decision tree classifier using scikit-learn that incorporates all equal states i.e. the root should have nine decisions available to the first player, then eight for the second player and so on alternating between players (1.0 for player 1, 2.0 for player 2). The second and inter-related question is how can i represent repeating data in a 0-8 (9) interval over and over again so that after the 9th interval has been read it will start over again from the root with the next game. It of course would be preferable to group together sub states that are the same for player 1 or player 2.
here is the pdf view of the tree generated by my code. Below is the code that i use to train the decision tree.
def visualise_tree(trained_tree):
dot_data = tree.export_graphviz(trained_tree,out_file=None)
graph = graphviz.Source(dot_data)
graph.render("oxo")
def trainTree(read_csv):
clf = tree.DecisionTreeClassifier()
slice_training_data = read_csv[["player","0", "1", "2", "3", "4", "5", "6", "7", "8"]]
slice_prediction_data = read_csv[["best_move"]]
clf.fit(slice_training_data,slice_prediction_data)
visualise_tree(clf)
print(read_csv)
if __name__ == "__main__":
""" Play a single game to the end using UCT for both players.
"""
#df = pd.DataFrame(columns=["player", "0", "1", "2", "3", "4", "5", "6", "7", "8", "best_move","won"])
#for i in range(1):
# df = UCTPlayGame(df)
read_csv = pd.read_csv('10000games.csv')
trainTree(read_csv)
#df = df[["player", "0", "1", "2", "3", "4", "5", "6", "7", "8", "best_move","won"]]
#print(df)
#df.to_csv('10000games.csv')
Here is the format of the data:
,player,0,1,2,3,4,5,6,7,8,best_move,won
0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,4,0
1,2.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0,0
2,1.0,2.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,1,0
3,2.0,2.0,1.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,7,0
4,1.0,2.0,1.0,0.0,0.0,1.0,0.0,0.0,2.0,0.0,3,0
5,2.0,2.0,1.0,0.0,1.0,1.0,0.0,0.0,2.0,0.0,5,0
6,1.0,2.0,1.0,0.0,1.0,1.0,2.0,0.0,2.0,0.0,2,0
7,2.0,2.0,1.0,1.0,1.0,1.0,2.0,0.0,2.0,0.0,6,0
8,1.0,2.0,1.0,1.0,1.0,1.0,2.0,2.0,2.0,0.0,8,0
0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0,0
as you can see 9 moves are made and then the dataset repeats itself for a new game (starting with 0). The data cycles between 1.0 and 2.0 for each player as each player takes it in turns to move. I additionally to the requirements added a won column for a set of moves that win the game (but unsure how to use this so i didn't include it in the prediction data). The decision tree should ideally merge all starting game states as described and predict what the best move should be.

Related

How to convert the position of an array into a single array?

What happens is that I have the following position of an array rows[0] = ["1", "2", "3", "4"] which stores certain data: what I want to do is to create a new array with the data that has the position 0 of the array rows so that, for example, I have a new vector = ["1", "2", "3", "4"], so that I could do, for example, console. log(new[0]); and have as output "1", and likewise with the other data of the new vector. I was thinking about doing it with some for or while loop, but I can't find the way to do it since row is of position 0.
you should try with destructuring
var rows = [];
rows[0] = ["1", "2", "3", "4"];
const [new] = rows
console.log(new[0]);

In Ruby, how do I calculate how close an element is to its neighbor?

I'm using Ruby 2.4. I have an array of strings, which are in fact numbers ...
["1", "2", "3", "7", "8", "9"]
How do I write an expression that will tell me the percentage of elements in the array (neglecting the last one) that have an absolute value difference of 1 with the element that follows it? So in the above, this is true for four out of the five elements, (since "7" has a difference of four following the "3" element), so I would expect the expression to return .8.
arr = ["1", "2", "3", "7", "8", "9"].map(&:to_i)
result = arr.each_cons(2).count { |a, b| (a - b).abs == 1 }
percentage = result / (arr.count - 1).to_f
#=> 0.8
Here are a couple of ways to do that.
arr = ["1", "2", "3", "7", "6", "8", "7"]
Step through the array and count the number of matches.
100 * arr[0..-2].each_index.count do |i|
ai, aip1 = arr[i], arr[i+1]
aip1 == ai.next || ai == aip1.next
end/(arr.size-1).to_f
#=> 66.66666666666667
Step with an enumerator
n = 0
enum = arr.to_enum
loop do
nxt, pk = enum.next, enum.peek
n += 1 if nxt.next == pk || pk.next == nxt
end
(100*n)/(arr.size-1).to_f
#=> 66.66666666666667
When all elements have been generated by the enumerator enum (which can also be defined arr.each) enum.peek raises a StopIteration exception (unless the array is empty, in which case enum.next raises the same exception). Kernel#loop handles that exception by breaking out of the loop.
Notice that there is no need to convert the strings to integers.

How would I group the "0 comparison value" of a ruby array after sorting them?

I have a list of players with ratings, wins, losses, and win loss percentages. I use sort_by to sort them by ratings then win loss percentages, and finally by number of wins. However, I also need to group the ones that are the same ranking together. According to the ruby sort_by docs here, the sorting block calculates a -1,0,+1. I need to group the results that are 0 according to the sorting algorithm.
I would like to then return them as a hash with rank as one key and the player as the other key. How would I start to accomplish that?
Thank you!
Here is code that I have so far but I just don't know how to group them together.
# User.rb
def self.all_rankings
sorted_rankings = self.searchable.sort_by do |user|
[-user.rating, -user.all_wins_losses_as_decimal, -user.all_wins]
end
rank = 1
sorted_rankings.map.with_index(1) do |user, index|
{
"rank": index,
"user": user
}
end
end
EDIT: Here is a sample of the desired API output. However, I would like to group the rank of the user together. So, for example, I sort based on the rating, all_wins_losses_as_decimal, and all_wins in that order. If two users have the same rating, all_wins_losses_as_decimal, and all_wins, then those two users should have the same rank.
"users": [
{
"id": 560,
"name": "Aaron Rodgers",
"rank": 1,
"z_rating": 40,
"all_wins": 2,
"all_losses": 0,
"all_wins_losses_percentage": "1.000"
},
{
"id": 559,
"name": "Director Sports",
"rank": 2,
"z_rating": 40,
"all_wins": 1,
"all_losses": 0,
"all_wins_losses_percentage": "1.000"
},
{
"id": 571,
"name": "Alford Schamberger",
"rank": 3,
"z_rating": 40,
"all_wins": 1,
"all_losses": 1,
"all_wins_losses_percentage": ".500"
}
]
I was thinking of accomplishing that by creating a hash of the user's rank and the user object to return the z_rating, all_wins, all_losses, and all_wins_losses_percentage.
The rating is an attribute of the User model. The all_wins_losses_as_decimal is a method on the User class which returns a Float type. The all_wins is another method on the User class which returns an Int
Not an expert and not sure if this would work but could you now simply Hash.select and lets say
h1.select { |key, value| value == '0' }
h1.select { |key, value| value == '1' }
and so on and you could asing value 0 -1 1 to whatever you want?
The 0 result is the result of comparison with <=> so you could always write a solution like
# User.rb
def self.all_rankings
sorted_rankings = self.searchable.sort_by do |user|
[-user.rating, -user.all_wins_losses_as_decimal, -user.all_wins]
end
rank = 1
rankings = Hash.new []
selected_rating = sorted_rankings[0]
sorted_rankings.each do |user|
if selected_rating <=> user != 0
selected_rating = user
rank += 1
end
rankings[rank] << ranking
end
return rankings
end
Effectively your output would be a hash with a hash with the rank as the key and the array of users as the value. Effectively grouping all users of equivalent rank.

How do I check that every element in an array is greater than the one before it?

I'm using Ruby 2.4. I have an array of strings, which are themselves numbers. So something like
["1", "2", "3", "5"]
How do I check that the integer version of every element in the array (except the first) is greater than the one before it? So for instance the function performed on the above would return true, but an array like
["1", "5", "4", "6"]
would return false (because "4" is not greater than "5".
An alternative way to phrase your predicate is: "For all consecutive pairs of numbers, is it true that the second is greater than the first"? This can be almost directly expressed in code:
ary.map(&:to_i).each_cons(2).all? {|first, second| second > first }
By the way: this property is called "strict monotonicity".
You can use Enumerable#sort_by to see if the arr is already sorted by numerical value:
arr = ["1", "2", "3", "5"]
arr.uniq.sort_by(&:to_i) == arr
#=> true
If the elements are not unique, then the array fails automatically. Since that means two elements are of the same value i.e. one is not greater than the other.

Most efficient way to match a certain number of items in a db.Model ListProperty

In reference to this different but not unrelated question I will borrow the example models.
class Foo(db.Model): bars = db.ListProperty(db.Key)
class Bar(db.Model): pass
If I have a certain Foo entity and I want to get all of the other foo entities also containing a certain bar Key in its bars ListProperty, I would use the following query:
related_foos = Foo.all().filter('bars', bar_entity).fetch(fetch_count)
What about if I want to find all other entities of model kind Foo that have at least N number of matching bar entities? The obvious way to do this with a for-loop would involve drastic inefficiencies, and it might be best to actually change the model itself to make this easier, but it doesn't seem obvious how to do so.
You can simply apply the same filter repeatedly:
related_foos = Foo.all().filter('bars', bar_entity).filter('bars', bar_entity_2).fetch(fetch_count)
Or, data-driven:
q = Foo.all()
for bar in bar_entities:
q.filter('bars', bar)
related_foos = q.fetch(fetch_count)
If you don't apply any inequalities or sort orders to the query, the datastore will be able to execute the queries using the built in indexes and the merge join strategy, regardless of how many filters you apply. If you need an inequality or sort order, however, you'll need to have an index for each number of bars you might want to filter on, which leads to exploding indexes (and so is best avoided!)
Given a foo record that has 10 bar_entities and looking for all foo records that have at least 2 of these 10 entities would result in 45 possible equality values 10!/(2!*(10-2)!)=45.
This can be deduced in 10_C_(2-1)=10 reads.
SELECT * from table WHERE bar="1" AND bar in ["2", "3", "4", "5", "6", "7", "8", "9", "0"]
SELECT * from table WHERE bar="2" AND bar in ["3", "4", "5", "6", "7", "8", "9", "0"]
SELECT * from table WHERE bar="3" AND bar in ["4", "5", "6", "7", "8", "9", "0"]
etc.
To reduce this to one read would require that when a foo record is added you populate a separate table that had all the 2 combinations for a given record.
Say you had
foo_table
foo1 [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
foo2 [1, 3, 4]
foo3 [1, 2, a]
foo4 [b, 6, c]
foo_combo_2_table
Parent Combination
foo1 12
foo1 13
... and all 45 foo1 combinations each in its own row
foo2 13
foo2 14
foo2 34
foo3 12
foo3 1a
foo3 2a
etc.
Now you can do a
indexes = SELECT __KEY__ from foo_combo_2_table WHERE combination IN [12, 13, 14, 15, ... all 45]
keys = [k.parent() for k in indexes] # you would need to filter for duplicates
This way you wont get into any exploding index issues.
If you also wanted to do any 3 or any 4 entities than for each of these you would need to create a foo_combo_n_table or do a 10_C_(n-1) number of reads.

Resources