10#define WIN32_LEAN_AND_MEAN
22 char* result=
SDL_iconv_string(
"UTF-8",
"WCHAR_T",
reinterpret_cast<const char*
>(
value.c_str()),(
value.size()+1)*
sizeof(
wchar_t));
39 std::string_view
trim(std::string_view
value)
41 const auto begin=
value.find_first_not_of(
" \t\r");
42 if(begin==std::string_view::npos)
return {};
43 return value.substr(begin,
value.find_last_not_of(
" \t\r")-begin+1);
49 if(
path.empty())
return std::unexpected(
"Player history path is empty");
50 std::array<HistoryEntry,100>
entries{};
52 std::error_code
error;
54 if(
error)
return std::unexpected(
error.message());
58 return std::unexpected(
"Player history INI exceeds read limit");
59 std::ifstream file(
path,std::ios::binary);
60 if(!file)
return std::unexpected(
"Cannot read player history INI");
61 std::string line;
int section=-1;
62 while(std::getline(file,line))
69 if(
name.starts_with(
"Player"))
79 if(
equals==std::string_view::npos)
continue;
87 if(file.bad())
return std::unexpected(
"Failed reading player history INI");
90 winnerProcessed_=
false;
94 std::expected<void,std::string> PlayerSelectionHistory::save(
const std::array<HistoryEntry,100>& entries)
96 if(!
configured())
return std::unexpected(
"Player history is not configured");
97 std::error_code
error;
98 if(!path_.parent_path().empty())std::filesystem::create_directories(path_.parent_path(),
error);
99 if(
error)
return std::unexpected(
error.message());
102 std::ofstream file(
temporary,std::ios::binary|std::ios::trunc);
103 if(!file)
return std::unexpected(
"Cannot write player history temporary INI");
109 if(
name.find_first_of(
"\r\n")!=std::string::npos)
return std::unexpected(
"Player history name contains a line break");
110 file<<
"[Player"<<
i+1<<
"]\r\nName="<<
name<<
"\r\nWins="<<
entry.wins
111 <<
"\r\nGreatestNetWorth="<<
entry.greatestNetWorth<<
"\r\n";
113 file.flush();
if(!file)
return std::unexpected(
"Failed writing player history INI");
117 return std::unexpected(
"Cannot publish player history INI: "+std::to_string(
GetLastError()));
120 if(
error)
return std::unexpected(
error.message());
128 for(
const auto& player:
players)
132 if(player.name.empty() || (player.aiLevel!=0 && player.local))
continue;
133 if(std::any_of(
next.begin(),
next.end(),[&](
const auto&
e){
return e.name==player.name;}))
continue;
134 auto free=std::find_if(
next.begin(),
next.end(),[](
const auto&
e){return e.name.empty();});
136 *
free={player.name,0,0};
138 if(
auto r=save(
next);!
r)
return r;
139 entries_=std::move(
next);winnerProcessed_=
false;
return {};
144 if(winnerProcessed_)
return {};
148 auto winner=std::find_if(
next.begin(),
next.end(),[&](
const auto&
e){return e.name==name;});
151 if(
winner->wins<std::numeric_limits<int>::max())++
winner->wins;
153 if(
auto r=save(
next);!
r)
return r;
156 entries_=std::move(
next);winnerProcessed_=
true;
return {};
161 std::vector<HistoryEntry> result;
163 std::stable_sort(result.begin(),result.end(),[](
const auto&
a,
const auto&
b)
164 {return a.wins>b.wins || (a.wins==b.wins && a.greatestNetWorth>b.greatestNetWorth);});
165 if(result.size()>8)result.resize(8);
170 std::vector<std::wstring> result;
171 for(
const auto&
entry:entries_)
if(!
entry.name.empty())result.push_back(
entry.name.substr(0,10));
std::array< rules::PlayerNumber, rules::MaxPlayers > players
std::deque< Bytes > output
const std::array< HistoryEntry, 100 > & entries() const noexcept
bool configured() const noexcept
std::expected< void, std::string > gameStarted(std::span< const HistoryPlayer > players)
std::vector< HistoryEntry > highScores() const
std::vector< std::wstring > names() const
std::expected< void, std::string > gameOver(std::wstring_view name, int worth, int aiLevel)
std::expected< void, std::string > open(std::filesystem::path path)