From: "Jesús Gabriel y Galán" Date: 2012-08-03T18:02:23+09:00 Subject: Re: Having difficulty processing text files - some techniques? On Fri, Aug 3, 2012 at 10:39 AM, b1_ __ wrote: > I am trying to extract information from a po-ker hand history text file > as an exercise in learning RegExp and ruby file manipulation classes IO > and File. > > The way the hand history text file is set out is like so: > > Hand No. xxxx - Date xxxx Time xxxx > Game type: xxxx Blinds level: xxxx > Table type: xxx > Player 1: xxxx (chip count) > Player 2: xxxx (chip count) > Player 3: xxxx (chip count) > Player 4: xxxx (chip count) > Hole Cards Phase > Player 1 calls > Player 2 raise > Player 3 folds > Flop Phase > Player 1 raises > Player 2 calls > Turn Phase > etc > etc > River Phase > etc > etc > Player 2 wins > > > > In my testing I have extracted successfully the very simple statistic of > VP$IP, which is the percentage of times a player Voluntarily Puts $ In > Pot pre-flop. So this is: > > Number of time called or raise pre-flop / Number of hands at the table > > My code (I used what I think is a Finite State Machine?): > > File.open("test_read.txt") do |f| > > count = 0 > state = nil > > while (line = f.gets) > case (state) > when nil > # Look for the words "Hole Cards" and if found turn on text > processing > if (line.match(/Hole Cards/)) > state = :parsing > end > when :parsing > # Look for word "Flop" or "wins" and if found stop processing text > if (line.match(/Flop/)) || (line.match(/wins/)) > state = nil > else > if (line.match(/#{name} calls/)) || (line.match(/#{name} > raises/)) then count += 1 end > end > end > end > > return count > > end > > This code processes text only when inside the Hole Card phase (pre-flop > phase) between lines with words "Hole Cards" (start text processing) and > "Flop" or "wins" (stop processing text). It increments a counter if it > finds the words "calls" and "raises" next to a player's name. This code > ran through the whole file, maybe over hundreds of hands, and extracted > the data. > > The problem I am having is that I need to have the extracted data > associate with hand data. Hand data might be the date of the hand was > played, the number of players in the hand, the blind level. This means I > need isolate each hand, extract the hand data and put it in a hash for > example, then extract stats like VP$IP for each player (a hand-by-hand > approach rather than a file-wide approach). So the output might go into > a text file specific to each player that would look like: > > Player 1 > VP$IP 2P 3P 4P All HandsAtTables > 2012-07-23 3 201 21 225 534 > 2012-07-24 45 10 3 58 1001 > 2012-07-25 5 5 5 15 420 > > Can I do this without storing data in arrays or objects? I've been > thinking I can use another Finite State Machine, only look for the words > "Hand" (start of hand) and "wins" (end of hand), the problem is I need > some of the data on the line that has the word "Hand" on it, something I > can't do with my code above. I also need to switch between hands when > there are no lines between the word "wins" and the next line which will > have the word "Hands" on it indicating the start of the next hand. I don't understand the part about not using arrays or objects. If you need to accumulate stuff while processing that's the only way. It seems that you want to keep separate counts by date and number of players. So I would use a hash whose key is a composite of both values (a struct with both fields for example), or maybe a hash of hashes. When you find the "Hand" line, you parse the date and the following lines to find out the number of players. With this you get the key to the hash. The value will be the count. Then you keep your current logic of being before the flop and counting. When you find another Hand, you first write the count to the hash with the current key, then recompute the next key based on the Hand line and number of players. Something like: Key = Struct.new :date, :number_of_players state = nil current_key = nil results = Hash.new(0) # keys will return a value of 0 when they don't exist while (line = f.gets) case (state) when nil if(line.match(/^Hand/) #find the date and the number of players in subsequent lines key = Key.new date,number_of_players state = :hand_found end when :hand_found # Look for the words "Hole Cards" and if found turn on text processing if (line.match(/Hole Cards/)) state = :parsing end when :parsing # Look for word "Flop" or "wins" and if found stop processing text, and store the current value if (line.match(/Flop/)) || (line.match(/wins/)) results[key] += count state = nil else if (line.match(/#{name} calls/)) || (line.match(/#{name} raises/)) then count += 1 end end end end end p results This is untested, but might give you an idea. Jesus.