diff --git a/features/restart_command.lua b/features/restart_command.lua index ee9d2966..b11f96fb 100644 --- a/features/restart_command.lua +++ b/features/restart_command.lua @@ -16,7 +16,8 @@ Public.game_types = game_types local memory = { mod_pack_text = '', restarting = nil, - use_map_poll_result = nil + use_map_poll_result = nil, + overwrite_mod_pack = nil } local start_game_data = { type = game_types.scenario, @@ -207,6 +208,14 @@ function Public.set_use_map_poll_result_option(state) memory.use_map_poll_result = state end +function Public.get_overwrite_modpack_option() + return memory.overwrite_mod_pack +end + +function Public.set_overwrite_modpack_option(state) + memory.overwrite_mod_pack = state +end + local main_frame_name = Gui.uid_name() local close_button_name = Gui.uid_name() local scenario_radio_button_name = Gui.uid_name() @@ -215,6 +224,7 @@ local name_textfield_name = Gui.uid_name() local set_mod_pack_checkbox_name = Gui.uid_name() local mod_pack_name_textfield_name = Gui.uid_name() local use_map_poll_result_checkbox_name = Gui.uid_name() +local overwrite_mod_pack_checkbox_name = Gui.uid_name() Public._main_frame_name = main_frame_name Public._close_button_name = close_button_name @@ -224,6 +234,7 @@ Public._name_textfield_name = name_textfield_name Public._set_mod_pack_checkbox_name = set_mod_pack_checkbox_name Public._mod_pack_name_textfield_name = mod_pack_name_textfield_name Public._use_map_poll_result_checkbox_name = use_map_poll_result_checkbox_name +Public._overwrite_mod_pack_checkbox_name = overwrite_mod_pack_checkbox_name local function value_of_type_or_deafult(value, value_type, default) if type(value) == value_type then @@ -345,6 +356,15 @@ local function draw_main_frame(player) } end + if memory.overwrite_mod_pack ~= nil then + main_frame.add { + type = 'checkbox', + name = overwrite_mod_pack_checkbox_name, + caption = 'Overwrite map poll mod pack', + state = memory.overwrite_mod_pack + } + end + local bottom_flow = main_frame.add { type = 'flow', direction = 'horizontal' @@ -404,6 +424,11 @@ Gui.on_checked_state_changed(use_map_poll_result_checkbox_name, function(event) memory.use_map_poll_result = use_map_poll_result_checkbox.state end) +Gui.on_checked_state_changed(overwrite_mod_pack_checkbox_name, function(event) + local overwrite_mod_pack_checkbox = event.element + memory.overwrite_mod_pack = overwrite_mod_pack_checkbox.state +end) + Gui.on_text_changed(mod_pack_name_textfield_name, function(event) local text = event.element.text start_game_data.mod_pack = text diff --git a/map_gen/maps/danger_ores/modules/map_poll.lua b/map_gen/maps/danger_ores/modules/map_poll.lua index d4abba38..5c80b38a 100644 --- a/map_gen/maps/danger_ores/modules/map_poll.lua +++ b/map_gen/maps/danger_ores/modules/map_poll.lua @@ -1,6 +1,7 @@ local Poll = require 'features.gui.poll' local Global = require 'utils.global' local Event = require 'utils.event' +local PollUtils = require 'utils.poll_utils' local Server = require 'features.server' local Ranks = require 'resources.ranks' @@ -16,63 +17,78 @@ end) local normal_mod_pack = 'danger_ore23' local bobs_mod_pack = 'danger_ore_bobs2' -local maps = {{ - name = 'danger-ore-deadlock-beltboxes-ore-only', - mod_pack = normal_mod_pack, - display_name = 'terraforming (default)' -}, { - name = 'danger-ore-one-direction-beltboxes-ore-only', - mod_pack = normal_mod_pack, - display_name = 'one direction (line)' -}, { - name = 'danger-ore-3way-beltboxes-ore-only', - mod_pack = normal_mod_pack, - display_name = '3-way (T shape)' -}, { - name = 'danger-ore-chessboard-beltboxes-ore-only', - mod_pack = normal_mod_pack, - display_name = 'chessboard (random squares)' -}, { - name = 'danger-ore-chessboard-uniform-beltboxes-ore-only', - mod_pack = normal_mod_pack, - display_name = 'chessboard uniform (fixed squares)' -}, { - name = 'danger-ore-circles-beltboxes-ore-only', - mod_pack = normal_mod_pack, - display_name = 'circles (ore rings)' -}, { - name = 'danger-ore-gradient-beltboxes-ore-only', - mod_pack = normal_mod_pack, - display_name = 'gradient (smooth ore ratios)' -}, { - name = 'danger-ore-split-beltboxes-ore-only', - mod_pack = normal_mod_pack, - display_name = 'split (4x sectors)' -}, { - name = 'danger-ore-hub-spiral-beltboxes-ore-only', - mod_pack = normal_mod_pack, - display_name = 'hub-spiral (with void)' -}, { - name = 'danger-ore-spiral-beltboxes-ore-only', - mod_pack = normal_mod_pack, - display_name = 'spiral (without void)' -}, { - name = 'danger-ore-landfill-beltboxes-ore-only', - mod_pack = normal_mod_pack, - display_name = 'landfill (all tiles)' -}, { - name = 'danger-ore-patches-beltboxes-ore-only', - mod_pack = normal_mod_pack, - display_name = 'patches (ore islands in coal)' -}, { - name = 'danger-ore-xmas-tree-beltboxes-ore-only', - mod_pack = normal_mod_pack, - display_name = 'xmas tree (triangle)' -}, { - name = 'danger-bobs-ores', - mod_pack = bobs_mod_pack, - display_name = 'bob\'s mod (default map)' -}} +local maps = { + { + name = 'danger-ore-deadlock-beltboxes-ore-only', + mod_pack = normal_mod_pack, + display_name = 'terraforming (default)' + }, + { + name = 'danger-ore-one-direction-beltboxes-ore-only', + mod_pack = normal_mod_pack, + display_name = 'one direction (line)' + }, + { + name = 'danger-ore-3way-beltboxes-ore-only', + mod_pack = normal_mod_pack, + display_name = '3-way (T shape)' + }, + { + name = 'danger-ore-chessboard-beltboxes-ore-only', + mod_pack = normal_mod_pack, + display_name = 'chessboard (random squares)' + }, + { + name = 'danger-ore-chessboard-uniform-beltboxes-ore-only', + mod_pack = normal_mod_pack, + display_name = 'chessboard uniform (fixed squares)' + }, + { + name = 'danger-ore-circles-beltboxes-ore-only', + mod_pack = normal_mod_pack, + display_name = 'circles (ore rings)' + }, + { + name = 'danger-ore-gradient-beltboxes-ore-only', + mod_pack = normal_mod_pack, + display_name = 'gradient (smooth ore ratios)' + }, + { + name = 'danger-ore-split-beltboxes-ore-only', + mod_pack = normal_mod_pack, + display_name = 'split (4x sectors)' + }, + { + name = 'danger-ore-hub-spiral-beltboxes-ore-only', + mod_pack = normal_mod_pack, + display_name = 'hub-spiral (with void)' + }, + { + name = 'danger-ore-spiral-beltboxes-ore-only', + mod_pack = normal_mod_pack, + display_name = 'spiral (without void)' + }, + { + name = 'danger-ore-landfill-beltboxes-ore-only', + mod_pack = normal_mod_pack, + display_name = 'landfill (all tiles)' + }, + { + name = 'danger-ore-patches-beltboxes-ore-only', + mod_pack = normal_mod_pack, + display_name = 'patches (ore islands in coal)' + }, + { + name = 'danger-ore-xmas-tree-beltboxes-ore-only', + mod_pack = normal_mod_pack, + display_name = 'xmas tree (triangle)' + }, + { + name = 'danger-bobs-ores', + mod_pack = bobs_mod_pack, + display_name = 'bob\'s mod (default map)' + } +} Event.add(Server.events.on_server_started, function() if data.created then @@ -107,32 +123,11 @@ function Public.get_next_map() end local answers = poll_data.answers - local vote_counts = {} - for i, answer_data in pairs(answers) do - vote_counts[i] = answer_data.voted_count - end - - local max_count = 0 - for i = 1, #vote_counts do - local count = vote_counts[i] or 0 - if count > max_count then - max_count = count - end - end - - if max_count == 0 then + local chosen_index = PollUtils.get_poll_winner(answers) + if chosen_index == nil then return nil end - local max_indexes = {} - for i = 1, #vote_counts do - local count = vote_counts[i] or 0 - if count == max_count then - max_indexes[#max_indexes + 1] = i - end - end - - local chosen_index = max_indexes[math.random(#max_indexes)] return maps[chosen_index] end diff --git a/map_gen/maps/danger_ores/modules/restart_command.lua b/map_gen/maps/danger_ores/modules/restart_command.lua index 03872f3d..a285136c 100644 --- a/map_gen/maps/danger_ores/modules/restart_command.lua +++ b/map_gen/maps/danger_ores/modules/restart_command.lua @@ -19,6 +19,7 @@ return function(config) Restart.set_start_game_data({type = Restart.game_types.scenario, name = config.scenario_name or 'danger-ore-next'}) Restart.set_use_map_poll_result_option(true) + Restart.set_overwrite_modpack_option(false) local function can_restart(player) if player.admin then @@ -202,7 +203,14 @@ return function(config) return end - Restart.set_start_game_data({type = Restart.game_types.scenario, name = map_data.name, mod_pack = map_data.mod_pack}) + local mod_pack_to_use + if Restart.get_overwrite_modpack_option() then + mod_pack_to_use = Restart.get_start_game_data().mod_pack + else + mod_pack_to_use = map_data.mod_pack + end + + Restart.set_start_game_data({type = Restart.game_types.scenario, name = map_data.name, mod_pack = mod_pack_to_use}) end Restart.register(can_restart, restart_callback, restart_requested) diff --git a/utils/poll_utils.lua b/utils/poll_utils.lua new file mode 100644 index 00000000..787381d4 --- /dev/null +++ b/utils/poll_utils.lua @@ -0,0 +1,37 @@ +local Public = {} + +--- Returns the index for the answer with the most votes. +-- If there is a tie one index from the ties is picked randomly usign rng +-- Returns nil if passed in zero answers. +-- rng defaults to math.random if not provided. +function Public.get_poll_winner(answers, rng) + local rand = rng or math.random + local vote_counts = {} + for i, answer_data in pairs(answers) do + vote_counts[i] = answer_data.voted_count or 0 + end + + local max_count = -math.huge + for i = 1, #vote_counts do + local count = vote_counts[i] + if count > max_count then + max_count = count + end + end + + if max_count == -math.huge then + return nil + end + + local max_indexes = {} + for i = 1, #vote_counts do + local count = vote_counts[i] + if count == max_count then + max_indexes[#max_indexes + 1] = i + end + end + + return max_indexes[rand(#max_indexes)] +end + +return Public diff --git a/utils/poll_utils_tests.lua b/utils/poll_utils_tests.lua new file mode 100644 index 00000000..2a226249 --- /dev/null +++ b/utils/poll_utils_tests.lua @@ -0,0 +1,96 @@ +local Declare = require 'utils.test.declare' +local PollUtils = require 'utils.poll_utils' +local Assert = require 'utils.test.assert' + +Declare.module({ + 'utils', + 'poll_utils', + 'get_poll_winner' +}, function() + Declare.test('picks most voted answer', function() + -- Arrange + local answers = { + { + voted_count = 0 + }, + { + voted_count = 3 + }, + { + voted_count = 1 + } + } + + -- Act + local actual = PollUtils.get_poll_winner(answers) + + -- Assert + Assert.equal(2, actual) + end) + + Declare.test('picks from tied answers', function() + -- Arrange + local answers = { + { + voted_count = 0 + }, + { + voted_count = 3 + }, + { + voted_count = 1 + }, + { + voted_count = 3 + } + } + + local rng = function(count) + Assert.equal(2, count) + return 2 -- pick last. + end + + -- Act + local actual = PollUtils.get_poll_winner(answers, rng) + + -- Assert + Assert.equal(4, actual) + end) + + Declare.test('picks from tied answers all zero', function() + -- Arrange + local answers = { + { + voted_count = 0 + }, + { + voted_count = 0 + }, + { + voted_count = 0 + } + } + + local rng = function(count) + Assert.equal(3, count) + return 2 -- pick middle. + end + + -- Act + local actual = PollUtils.get_poll_winner(answers, rng) + + -- Assert + Assert.equal(2, actual) + end) + + Declare.test('returns nil when no answers', function() + -- Arrange + local answers = {} + + -- Act + local actual = PollUtils.get_poll_winner(answers) + + -- Assert + Assert.equal(nil, actual) + end) +end)