Diablo3 Farming + Python
I haven’t played that much Diablo 3 recently, but since the last patch I’ve done a little bit of Demonic Essence farming in Warrior’s Rest. To get an idea of how efficient it is, I wrote a really hack’ish Python script to track some stats and used Tim Golden’s example code for catching global key presses in Windows. The script allowed me to press Shift+F1 or Shift+F2 while inside Diablo 3 to record a time stamp and whether or not I got an essence in the previous run. Note that the code is pretty horrible, but it was a 20 minute project and I only needed something quick and dirty.
Code
import os
import sys
import ctypes
from ctypes import wintypes
import win32con
import time
import datetime
import winsound
byref = ctypes.byref
user32 = ctypes.windll.user32
HOTKEYS = {
1: (win32con.VK_F3, win32con.MOD_WIN),
2: (win32con.VK_F4, win32con.MOD_WIN),
3: (win32con.VK_F1, win32con.MOD_SHIFT),
4: (win32con.VK_F2, win32con.MOD_SHIFT)
}
def handle_win_f3():
os.startfile(os.environ['TEMP'])
def handle_win_f4():
user32.PostQuitMessage(0)
################
LAST_TIMESTAMP = False
RUNS = 0
ESSENCES = 0
def handle_shift_f1():
record_run(True)
def handle_shift_f2():
record_run(False)
def record_run(got_essence):
global LAST_TIMESTAMP
global RUNS
global ESSENCES
RUNS += 1
if got_essence:
ESSENCES += 1
timestamp = int(time.time())
date_str = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
drop_rate = 0
if ESSENCES > 0:
drop_rate = (ESSENCES / float(RUNS)) * 100
run_length = 0
if LAST_TIMESTAMP is not False:
run_length = timestamp - LAST_TIMESTAMP
if got_essence:
print 'DROP - %d - %s - %d/%d - %0.2f%% - %d' % (timestamp, date_str, ESSENCES, RUNS, drop_rate, run_length)
else:
print 'NULL - %d - %s - %d/%d - %0.2f%% - %d' % (timestamp, date_str, ESSENCES, RUNS, drop_rate, run_length)
winsound.PlaySound('CallWaiting.wav', winsound.SND_FILENAME)
LAST_TIMESTAMP = timestamp
################
HOTKEY_ACTIONS = {
1: handle_win_f3,
2: handle_win_f4,
3: handle_shift_f1,
4: handle_shift_f2
}
# RegisterHotKey takes:
# Window handle for WM_HOTKEY messages (None = this thread)
# arbitrary id unique within the thread
# modifiers (MOD_SHIFT, MOD_ALT, MOD_CONTROL, MOD_WIN)
# VK code (either ord ('x') or one of win32con.VK_*)
for id, (vk, modifiers) in HOTKEYS.items ():
print "Registering id", id, "for key", vk
if not user32.RegisterHotKey (None, id, modifiers, vk):
print "Unable to register id", id
# Home-grown Windows message loop: does
# just enough to handle the WM_HOTKEY
# messages and pass everything else along.
try:
msg = wintypes.MSG()
while user32.GetMessageA(byref(msg), None, 0, 0) != 0:
if msg.message == win32con.WM_HOTKEY:
action_to_take = HOTKEY_ACTIONS.get(msg.wParam)
if action_to_take:
action_to_take()
user32.TranslateMessage(byref(msg))
user32.DispatchMessageA(byref(msg))
finally:
for id in HOTKEYS.keys():
user32.UnregisterHotKey(None, id)
Runs
C:\Python27\python.exe D:/Dropbox/Code/py-farm-companion/companion.py
Registering id 1 for key 114
Registering id 2 for key 115
Registering id 3 for key 112
Registering id 4 for key 113
DROP - 1363409462 - 2013-03-16 15:51 - 1/1 - 100.00% - 0
NULL - 1363409526 - 2013-03-16 15:52 - 1/2 - 50.00% - 64
DROP - 1363409594 - 2013-03-16 15:53 - 2/3 - 66.67% - 68
NULL - 1363409660 - 2013-03-16 15:54 - 2/4 - 50.00% - 66
NULL - 1363409742 - 2013-03-16 15:55 - 2/5 - 40.00% - 82
NULL - 1363409808 - 2013-03-16 15:56 - 2/6 - 33.33% - 66
DROP - 1363409870 - 2013-03-16 15:57 - 3/7 - 42.86% - 62
NULL - 1363409923 - 2013-03-16 15:58 - 3/8 - 37.50% - 53
NULL - 1363409980 - 2013-03-16 15:59 - 3/9 - 33.33% - 57
NULL - 1363410035 - 2013-03-16 16:00 - 3/10 - 30.00% - 55
DROP - 1363410098 - 2013-03-16 16:01 - 4/11 - 36.36% - 63
DROP - 1363410158 - 2013-03-16 16:02 - 5/12 - 41.67% - 60
DROP - 1363410216 - 2013-03-16 16:03 - 6/13 - 46.15% - 58
NULL - 1363410277 - 2013-03-16 16:04 - 6/14 - 42.86% - 61
DROP - 1363410340 - 2013-03-16 16:05 - 7/15 - 46.67% - 63
NULL - 1363410456 - 2013-03-16 16:07 - 7/16 - 43.75% - 116
NULL - 1363410515 - 2013-03-16 16:08 - 7/17 - 41.18% - 59
NULL - 1363410578 - 2013-03-16 16:09 - 7/18 - 38.89% - 63
DROP - 1363410656 - 2013-03-16 16:10 - 8/19 - 42.11% - 78
DROP - 1363410768 - 2013-03-16 16:12 - 9/20 - 45.00% - 112
DROP - 1363410837 - 2013-03-16 16:13 - 10/21 - 47.62% - 69
DROP - 1363410899 - 2013-03-16 16:14 - 11/22 - 50.00% - 62
NULL - 1363410953 - 2013-03-16 16:15 - 11/23 - 47.83% - 54
NULL - 1363411016 - 2013-03-16 16:16 - 11/24 - 45.83% - 63
DROP - 1363411076 - 2013-03-16 16:17 - 12/25 - 48.00% - 60
NULL - 1363411168 - 2013-03-16 16:19 - 12/26 - 46.15% - 92
NULL - 1363411244 - 2013-03-16 16:20 - 12/27 - 44.44% - 76
NULL - 1363411306 - 2013-03-16 16:21 - 12/28 - 42.86% - 62
DROP - 1363411375 - 2013-03-16 16:22 - 13/29 - 44.83% - 69
NULL - 1363411454 - 2013-03-16 16:24 - 13/30 - 43.33% - 79
NULL - 1363411523 - 2013-03-16 16:25 - 13/31 - 41.94% - 69
NULL - 1363411582 - 2013-03-16 16:26 - 13/32 - 40.62% - 59
DROP - 1363411645 - 2013-03-16 16:27 - 14/33 - 42.42% - 63
NULL - 1363411699 - 2013-03-16 16:28 - 14/34 - 41.18% - 54
NULL - 1363411764 - 2013-03-16 16:29 - 14/35 - 40.00% - 65
NULL - 1363411821 - 2013-03-16 16:30 - 14/36 - 38.89% - 57
DROP - 1363411879 - 2013-03-16 16:31 - 15/37 - 40.54% - 58
DROP - 1363411942 - 2013-03-16 16:32 - 16/38 - 42.11% - 63
DROP - 1363412001 - 2013-03-16 16:33 - 17/39 - 43.59% - 59
NULL - 1363412067 - 2013-03-16 16:34 - 17/40 - 42.50% - 66
DROP - 1363412145 - 2013-03-16 16:35 - 18/41 - 43.90% - 78
DROP - 1363412223 - 2013-03-16 16:37 - 19/42 - 45.24% - 78
NULL - 1363412293 - 2013-03-16 16:38 - 19/43 - 44.19% - 70
NULL - 1363412360 - 2013-03-16 16:39 - 19/44 - 43.18% - 67
DROP - 1363412422 - 2013-03-16 16:40 - 20/45 - 44.44% - 62
NULL - 1363412485 - 2013-03-16 16:41 - 20/46 - 43.48% - 63
DROP - 1363412548 - 2013-03-16 16:42 - 21/47 - 44.68% - 63
DROP - 1363412611 - 2013-03-16 16:43 - 22/48 - 45.83% - 63
NULL - 1363412681 - 2013-03-16 16:44 - 22/49 - 44.90% - 70
DROP - 1363412752 - 2013-03-16 16:45 - 23/50 - 46.00% - 71
NULL - 1363412815 - 2013-03-16 16:46 - 23/51 - 45.10% - 63
NULL - 1363412880 - 2013-03-16 16:48 - 23/52 - 44.23% - 65
DROP - 1363412953 - 2013-03-16 16:49 - 24/53 - 45.28% - 73
DROP - 1363413020 - 2013-03-16 16:50 - 25/54 - 46.30% - 67
DROP - 1363413090 - 2013-03-16 16:51 - 26/55 - 47.27% - 70
NULL - 1363413156 - 2013-03-16 16:52 - 26/56 - 46.43% - 66
NULL - 1363413209 - 2013-03-16 16:53 - 26/57 - 45.61% - 53
DROP - 1363413274 - 2013-03-16 16:54 - 27/58 - 46.55% - 65
NULL - 1363413349 - 2013-03-16 16:55 - 27/59 - 45.76% - 75
NULL - 1363413417 - 2013-03-16 16:56 - 27/60 - 45.00% - 68
DROP - 1363413479 - 2013-03-16 16:57 - 28/61 - 45.90% - 62
DROP - 1363413548 - 2013-03-16 16:59 - 29/62 - 46.77% - 69
NULL - 1363413608 - 2013-03-16 17:00 - 29/63 - 46.03% - 60
NULL - 1363413669 - 2013-03-16 17:01 - 29/64 - 45.31% - 61
NULL - 1363413735 - 2013-03-16 17:02 - 29/65 - 44.62% - 66
DROP - 1363413801 - 2013-03-16 17:03 - 30/66 - 45.45% - 66
Summary
In the end I ran Warrior’s Rest on MP9 66 times with my
Barbarian. The average run time was 67 seconds (timed from pressing resume to existing back to the lobby screen). I got 30 essences (45%) which is quite a bit higher than the expected drop rate (35%). Overall I got 1 essences every 2:24, which doesn’t really sound that great. I think I need to tweak my build and items (I really want Earthquake, but I don’t want to give up the healing from Rend). Maybe I’ll time some Vault of the Assassin runs next time.