| 40 | When a Quagent is instantiated via the 'new' method, it connects to the ioquake server. The 'write' method can be used to issue commands through the socket. (Notice that protocol zero op codes have been mapped to more user-friendly strings.) |
| 41 | |
| 42 | {{{ |
| 43 | def whereis (quagent) |
| 44 | quagent.write("now", # scheduling |
| 45 | "location", # op |
| 46 | "", # args |
| 47 | nil, # initial value |
| 48 | lambda { |prev, data| # reduction function |
| 49 | return data_to_doubles(data) |
| 50 | }) |
| 51 | end |
| 52 | }}} |
| 53 | |
| 54 | The write method takes the arguments "now", "location", and "" and sends the string "n lc #" (where # is an integer) to the server. It then creates a separate thread that reads replies from the socket. When the reply is of type "rs", 'write' calls its last argument on the accumulator (initially the penultimate argument) and the data from the last response (an array of strings). When the reply is of type "cp", 'write' terminates the thread and returns the accumulator. In the previous example, there was no need to specify an initial value since the server only sends data once. The radar op, however, sends data for every object it finds. The 'scan' function uses a hash map as the initial value and then builds a list of (relative) locations for each item type. |
| 55 | |
| 56 | {{{ |
| 57 | def scan (quagent, radius) |
| 58 | quagent.write("now", |
| 59 | "radar", |
| 60 | "#{radius}", |
| 61 | {}, |
| 62 | lambda { |prev, data| |
| 63 | id, type, dist, yaw, pitch = data |
| 64 | if prev[type] == nil |
| 65 | return prev.merge({type => [data_to_doubles [dist, yaw, pitch]]}) |
| 66 | end |
| 67 | return prev.merge({type => prev[type].concat([data_to_doubles [dist, yaw, pitch]])}) |
| 68 | }) |
| 69 | end |
| 70 | }}} |