WIP: xlua: add NHQP contest script
3 files changed, 41 insertions(+), 89 deletions(-)

M xlua/CMakeLists.txt
M xlua/scripts/.hgignore
M xlua/scripts/contests/NEQP.lua => xlua/scripts/contests/NHQP.lua
M xlua/CMakeLists.txt +1 -0
@@ 28,6 28,7 @@ set(LUA_SCRIPTS
 	contests/CQ-WW-VHF
 	contests/K1USN-SST
 	contests/NEQP
+	contests/NHQP
 	contests/POTA
 	helper-county
 	prompt

          
M xlua/scripts/.hgignore +1 -0
@@ 5,6 5,7 @@ contests/ARRL-VHF-{JAN,JUN,SEP}.{luac,c}
 contests/CQ-WW-VHF.{luac,c}
 contests/K1USN-SST.{luac,c}
 contests/NEQP.{luac,c}
+contests/NHQP.{luac,c}
 contests/POTA.{luac,c}
 helper-county.{luac,c}
 prompt.{luac,c}

          
M xlua/scripts/contests/NEQP.lua => xlua/scripts/contests/NHQP.lua +39 -89
@@ 35,47 35,11 @@ local mode_points = {
 }
 
 local raw_county_data = {
-	-- NB: these are the CT multiplies starting 2024; previous years
-	-- used counties instead of Regional Councils of Government
-	{ 291, 'CT', 'CTGBR', 'Greater Bridgeport' },	{ 291, 'CT', 'CTLCR', 'Lower CT River' },
-	{ 291, 'CT', 'CTNAU', 'Naugatuck Valley' },	{ 291, 'CT', 'CTNOE', 'Northeastern' },
-	{ 291, 'CT', 'CTNOW', 'Northwest Hills' },	{ 291, 'CT', 'CTSOE', 'Southeastern' },
-	{ 291, 'CT', 'CTSOC', 'South Central' },	{ 291, 'CT', 'CTWES', 'Western' },
-
-	{ 291, 'MA', 'MABAR', 'Barnstable' },		{ 291, 'MA', 'MABER', 'Berkshire' },
- 	{ 291, 'MA', 'MABRI', 'Bristol' },		{ 291, 'MA', 'MADUK', 'Dukes' },
- 	{ 291, 'MA', 'MAESS', 'Essex' },		{ 291, 'MA', 'MAFRA', 'Franklin' },
- 	{ 291, 'MA', 'MAHMD', 'Hampden' },		{ 291, 'MA', 'MAHMP', 'Hampshire' },
- 	{ 291, 'MA', 'MAMID', 'Middlesex' },		{ 291, 'MA', 'MANAN', 'Nantucket' },
- 	{ 291, 'MA', 'MANOR', 'Norfolk' },		{ 291, 'MA', 'MAPLY', 'Plymouth' },
- 	{ 291, 'MA', 'MASUF', 'Suffolk' },		{ 291, 'MA', 'MAWOR', 'Worcester' },
-
-	{ 291, 'ME', 'MEAND', 'Androscoggin' },		{ 291, 'ME', 'MEARO', 'Aroostook' },
- 	{ 291, 'ME', 'MECUM', 'Cumberland' },		{ 291, 'ME', 'MEFRA', 'Franklin' },
- 	{ 291, 'ME', 'MEHAN', 'Hancock' },		{ 291, 'ME', 'MEKEN', 'Kennebec' },
- 	{ 291, 'ME', 'MEKNO', 'Knox' },			{ 291, 'ME', 'MELIN', 'Lincoln' },
- 	{ 291, 'ME', 'MEOXF', 'Oxford' },		{ 291, 'ME', 'MEPEN', 'Penobscot' },
- 	{ 291, 'ME', 'MEPIS', 'Piscataquis' },		{ 291, 'ME', 'MESAG', 'Sagadahoc' },
- 	{ 291, 'ME', 'MESOM', 'Somerset' },		{ 291, 'ME', 'MEWAL', 'Waldo' },
- 	{ 291, 'ME', 'MEWAS', 'Washington' },		{ 291, 'ME', 'MEYOR', 'York' },
-
-	{ 291, 'NH', 'NHBEL', 'Belknap' },		{ 291, 'NH', 'NHCAR', 'Carroll' },
- 	{ 291, 'NH', 'NHCHE', 'Cheshire' },		{ 291, 'NH', 'NHCOO', 'Coos' },
- 	{ 291, 'NH', 'NHGRA', 'Grafton' },		{ 291, 'NH', 'NHHIL', 'Hillsborough' },
- 	{ 291, 'NH', 'NHMER', 'Merrimack' },		{ 291, 'NH', 'NHROC', 'Rockingham' },
- 	{ 291, 'NH', 'NHSTR', 'Strafford' },		{ 291, 'NH', 'NHSUL', 'Sullivan' },
-
-	{ 291, 'RI', 'RIBRI', 'Bristol' },		{ 291, 'RI', 'RIKEN', 'Kent' },
- 	{ 291, 'RI', 'RINEW', 'Newport' },		{ 291, 'RI', 'RIPRO', 'Providence' },
- 	{ 291, 'RI', 'RIWAS', 'Washington' },
-
-	{ 291, 'VT', 'VTADD', 'Addison' },		{ 291, 'VT', 'VTBEN', 'Bennington' },
- 	{ 291, 'VT', 'VTCAL', 'Caledonia' },		{ 291, 'VT', 'VTCHI', 'Chittenden' },
- 	{ 291, 'VT', 'VTESS', 'Essex' },		{ 291, 'VT', 'VTFRA', 'Franklin' },
- 	{ 291, 'VT', 'VTGRA', 'Grand Isle' },		{ 291, 'VT', 'VTLAM', 'Lamoille' },
- 	{ 291, 'VT', 'VTORA', 'Orange' },		{ 291, 'VT', 'VTORL', 'Orleans' },
- 	{ 291, 'VT', 'VTRUT', 'Rutland' },		{ 291, 'VT', 'VTWAS', 'Washington' },
- 	{ 291, 'VT', 'VTWNH', 'Windham' },		{ 291, 'VT', 'VTWND', 'Windsor' },
+	{ 291, 'NH', 'BEL', 'Belknap' },	{ 291, 'NH', 'CAR', 'Carroll' },
+ 	{ 291, 'NH', 'CHE', 'Cheshire' },	{ 291, 'NH', 'COO', 'Coos' },
+ 	{ 291, 'NH', 'GRA', 'Grafton' },	{ 291, 'NH', 'HIL', 'Hillsborough' },
+ 	{ 291, 'NH', 'MER', 'Merrimack' },	{ 291, 'NH', 'ROC', 'Rockingham' },
+ 	{ 291, 'NH', 'STR', 'Strafford' },	{ 291, 'NH', 'SUL', 'Sullivan' },
 }
 
 local function duphash(qso, tx, rx)

          
@@ 87,7 51,7 @@ local function duphash(qso, tx, rx)
 			     rx)
 end
 
-local neqp = hlog.contest_helper.county(raw_county_data, { 1, 291 }, duphash)
+local nhqp = hlog.contest_helper.county(raw_county_data, { 1, 6, 110, 291 }, duphash)
 
 local function startup_fill_template(template)
 	local done = false

          
@@ 98,11 62,11 @@ local function startup_fill_template(tem
 
 		template.tx.dxcc = io.stdin:read()
 
-		if neqp:dxcc_use_state(template.tx.dxcc) then
+		if nhqp:dxcc_use_state(template.tx.dxcc) then
 			print("Enter state or province (e.g., MA):")
 
 			local tmp = io.stdin:read():upper()
-			local state, county = neqp:abbrev2name(tmp)
+			local state, county = nhqp:abbrev2name(tmp)
 
 			if tmp == "" or tmp == nil then
 				-- try again

          
@@ 110,18 74,9 @@ local function startup_fill_template(tem
 				template.tx.state = state
 				template.tx.county = county
 				done = true
-			elseif neqp:state_use_county(template.tx.dxcc, tmp) then
-				print("Enter county abbreviation (e.g., MID):")
-
-				local tmp2 = io.stdin:read():upper()
-
-				state, county = neqp:abbrev2name(tmp .. tmp2)
-				if county == nil then
-					-- fall back to just trying the last
-					-- input in case the user entered the
-					-- whole abbreviation (e.g., MAMID)
-					state, county = neqp:abbrev2name(tmp2)
-				end
+			elseif state == "NH" then
+				print("Enter county abbreviation (e.g., ROC):")
+				state, county = nhqp:abbrev2name(io.stdin:read():upper())
 
 				if county ~= nil then
 					template.tx.state = state

          
@@ 179,7 134,7 @@ local function field_changed(qso, fields
 		qso.additional['contest-rx'] = value
 
 		-- set state & county based on the exchange
-		local state, county = neqp:abbrev2name(value)
+		local state, county = nhqp:abbrev2name(value)
 		qso.rx.state = state
 		qso.rx.county = county
 

          
@@ 192,7 147,7 @@ local function field_changed(qso, fields
 	end
 
 	return nil, {
-		["DUP"] = neqp:is_dup(qso) and "red" or "black",
+		["DUP"] = nhqp:is_dup(qso) and "red" or "black",
 		["BAND"] = allowed_bands[qso.tx.band] and "black" or "red",
 	}
 end

          
@@ 225,43 180,34 @@ local function qpoints(qso, tx, rx)
 end
 
 local function qmult(qso, tx, rx)
-	local function in_ne(side)
-		if not neqp:dxcc_use_county(side.dxcc) and
-		   not neqp:dxcc_use_state(side.dxcc) then
-			return false -- DX
-		end
-
-		if not neqp:state_use_county(side.dxcc, side.state) then
-			return false -- state/province outside of New England
-		end
-
-		return true -- inside New England
+	local function in_nh(side)
+		return side.dxcc == 291 and side.state == "NH"
 	end
 
-	if in_ne(qso.tx) then
-		-- We are in New England; we get to work anyone anywhere.
-	elseif in_ne(qso.rx) then
-		-- We are outside of New England and other station is in New
-		-- England, we get to work them.
+	if in_nh(qso.tx) then
+		-- We are in New Hampshire; we get to work anyone anywhere.
+	elseif in_nh(qso.rx) then
+		-- We are outside of New Hampshire and other station is in
+		-- New Hampshire, we get to work them.
 	else
-		-- We are outside of New England and the other station is
-		-- also outside of New England.
+		-- We are outside of New Hampshire and the other station is
+		-- also outside of New Hampshire.
 		return nil
 	end
 
 	-- At this point, we know that this contact involves at least one
-	-- New England station.
+	-- New Hampshire station.
 
-	if neqp:state_use_county(qso.rx.dxcc, qso.rx.state) then
+	if nhqp:state_use_county(qso.rx.dxcc, qso.rx.state) then
 		-- Other station should have given us state & county.
-		local state, county = neqp:abbrev2name(rx)
+		local state, county = nhqp:abbrev2name(rx)
 
 		if county == nil then
 			return nil -- no/bad state & county code given
 		end
 
 		return rx -- state & county code is valid
-	elseif neqp:dxcc_use_state(qso.rx.dxcc) then
+	elseif nhqp:dxcc_use_state(qso.rx.dxcc) then
 		-- Other station should have given us a state/province.
 		return rx -- TODO: sanity check state/province
 	else

          
@@ 281,13 227,17 @@ local function export(outfname)
 	local points
 	local mults
 
-	all, station, ops, points, mults = neqp:export_prep(contest.id, contest.year,
+	all, station, ops, points, mults = nhqp:export_prep(contest.id, contest.year,
 							    qpoints, qmult, qexchange)
 
+	-- FIXME: limit to 10 DXCCs
+	print(mults:tostring())
+	mults = mults:count_items()
+
 	hlog.cabrillo.print_header(contest.id,
 				   station,
 				   ops:as_array(),
-				   points * mults:count_items())
+				   points * mults)
 
 	for _, rec in ipairs(all) do
 		local qso = rec[1]

          
@@ 328,15 278,15 @@ return {
 
 		-- called once on startup (after config's fill_template)
 		startup = function(template)
-			print("NEQP contest script")
+			print("NHQP contest script")
 			startup_fill_template(template)
 
 			-- load previous contacts from history
 			for qso in hlog.index.history(true) do
-				if qso.additional['contest-id'] == "NEQP" and
+				if qso.additional['contest-id'] == "NHQP" and
 				   qso.additional['contest-year'] == contest.year and
 				   allowed_bands[qso.tx.band] then
-					neqp:qso_done(qso)
+					nhqp:qso_done(qso)
 				end
 			end
 		end,

          
@@ 349,7 299,7 @@ return {
 			local dxcc = qso.tx.dxcc
 			local state = qso.tx.state
 			local county = qso.tx.county
-			local exch = neqp:name2abbrev(dxcc, state, county)
+			local exch = nhqp:name2abbrev(dxcc, state, county)
 
 			-- qso.tx.rst = "59" -- we always send 59
 			-- qso.rx.rst = "59" -- we expect a 59

          
@@ 357,17 307,17 @@ return {
 			if exch == nil then
 				contest.lcd("?")
 			else
-				qso.additional['contest-id'] = 'NEQP'
+				qso.additional['contest-id'] = 'NHQP'
 				qso.additional['contest-year'] = contest.year
 				qso.additional['contest-tx'] = exch
-				contest.lcd(exch)
+				contest.lcd("59 " .. exch)
 			end
 		end,
 
 		-- called when QSO is complete & saved, just prior to a reset
 		qso_done = function(qso)
 			if allowed_bands[qso.tx.band] then
-				neqp:qso_done(qso)
+				nhqp:qso_done(qso)
 			end
 		end,