1+ #include " advent_of_code/day_9.hxx"
2+ #include " spdlog/spdlog.h"
3+ #include < algorithm>
4+ #include < deque>
5+ #include < exception>
6+ #include < iostream>
7+ #include < memory>
8+ #include < numeric>
9+ #include < optional>
10+ #include < stdexcept>
11+ #include < string>
12+ #include < unordered_map>
13+
14+ namespace AdventOfCode24 ::Day9 {
15+ DiskMap get_disk_map (const std::string& entry) {
16+ if (entry.empty ()) {
17+ throw std::runtime_error (" Cannot process empty string." );
18+ }
19+ std::istringstream isstr;
20+ isstr.str (entry);
21+ int number{-1 };
22+ int line_pos{0 };
23+ int block_index{0 };
24+ int index{0 };
25+ layout disk_map;
26+
27+ for (const char & number_str : entry) {
28+ try {
29+ number = std::stoi (std::string (1 , number_str));
30+ } catch (std::exception& e) {
31+ throw std::runtime_error (" Could not convert value " + std::string (1 , number_str));
32+ }
33+
34+ if (line_pos % 2 == 1 ) {
35+ disk_map.emplace (-1 , std::deque<int >());
36+ for (int n{0 }; n < number; ++n) {
37+ disk_map.at (-1 ).push_back (index);
38+ index++;
39+ }
40+ } else {
41+ disk_map.emplace (block_index, std::deque<int >());
42+ for (int i{0 }; i < number; ++i) {
43+ disk_map.at (block_index).push_back (index);
44+ index++;
45+ }
46+ block_index++;
47+ }
48+
49+ line_pos++;
50+ }
51+
52+ const int last_block_index{static_cast <int >(disk_map.at (block_index - 1 ).size ()) - 1 };
53+
54+ return DiskMap (disk_map, index, block_index - 1 , last_block_index);
55+ }
56+
57+ void DiskMap::print () const {
58+ std::vector<std::string> output_vec (size, " X" );
59+
60+ for (const auto & map_entry : disk_map) {
61+ for (const int & pos : map_entry.second ) {
62+ if (pos >= size) {
63+ throw std::runtime_error (
64+ " Cannot print map, recorded position " + std::to_string (pos) +
65+ " is not within specified map size " + std::to_string (size));
66+ }
67+ output_vec[pos] = ((map_entry.first == -1 ) ? " ." : std::to_string (map_entry.first ));
68+ }
69+ }
70+
71+ for (const std::string& output : output_vec) {
72+ std::cout << output;
73+ }
74+
75+ std::cout << std::endl;
76+ }
77+
78+ bool DiskMap::block_cleanup_iteration () {
79+ layout disk_layout{disk_map};
80+
81+ if (disk_layout.at (-1 ).empty ()) return {};
82+
83+ const int first_empty = disk_layout.at (-1 )[0 ];
84+ const int current_pos = disk_layout.at (last_block_id)[last_block_index];
85+
86+ if (current_pos < first_empty) return false ;
87+
88+ disk_layout.at (last_block_id)[last_block_index] = first_empty;
89+ disk_layout.at (-1 ).pop_front ();
90+ disk_layout.at (-1 ).push_back (current_pos);
91+
92+ int new_last_block{-1 };
93+ int new_last_index{-1 };
94+
95+ if (last_block_index < 1 ) {
96+ new_last_block = last_block_id - 1 ;
97+ if (new_last_block < 0 ) return false ;
98+ new_last_index = disk_layout.at (new_last_block).size () - 1 ;
99+ } else {
100+ new_last_block = last_block_id;
101+ new_last_index = last_block_index - 1 ;
102+ }
103+
104+ last_block_index = new_last_index;
105+ last_block_id = new_last_block;
106+ disk_map = disk_layout;
107+
108+ return true ;
109+ }
110+
111+ std::optional<int > DiskMap::get_first_empty (const int & size) const {
112+ auto find_empties = disk_map.find (-1 );
113+
114+ if (find_empties == disk_map.end () || disk_map.at (-1 ).empty ()) {
115+ return {};
116+ }
117+
118+ for (int i{0 }; i < disk_map.at (-1 ).size (); ++i) {
119+ int offset{0 };
120+ while (disk_map.at (-1 )[i + offset + 1 ] - disk_map.at (-1 )[i + offset] == 1 ) {
121+ offset++;
122+ }
123+ if (offset + 1 >= size) return i;
124+ }
125+
126+ return {};
127+ }
128+
129+ std::optional<int > DiskMap::file_cleanup_iteration (const int & block_index) {
130+ if (block_index < 0 ) {
131+ spdlog::error (" Block index < 0" );
132+ return {};
133+ }
134+ if (disk_map.find (-1 ) == disk_map.end ()) {
135+ spdlog::error (" No blank space found!" );
136+ return {};
137+ }
138+
139+ const size_t size{disk_map.at (block_index).size ()};
140+ std::sort (disk_map.at (-1 ).begin (), disk_map.at (-1 ).end ());
141+
142+ const std::optional<int > check_for_empty{get_first_empty (size)};
143+
144+ if (!check_for_empty.has_value ()) {
145+ spdlog::warn (" Could not find any empty space to accomodate file " + std::to_string (block_index) + " of size " + std::to_string (size));
146+ return block_index - 1 ;
147+ }
148+
149+ const int first_empty_index{disk_map.at (-1 )[check_for_empty.value ()]};
150+
151+ std::deque<int > block_positions{disk_map.at (block_index)};
152+
153+ if (block_positions.empty ()) {
154+ spdlog::warn (" Block " + std::to_string (block_index) + " is empty." );
155+ return block_index - 1 ;
156+ }
157+
158+ std::sort (block_positions.begin (), block_positions.end ());
159+
160+ if (block_positions[0 ] <= first_empty_index) {
161+ spdlog::info (" Block " + std::to_string (block_index) + " already at optimal position." );
162+ return block_index - 1 ;
163+ }
164+
165+ disk_map.at (block_index).clear ();
166+
167+ for (int i{0 }; i < size; ++i) {
168+ disk_map.at (block_index).push_back (first_empty_index + i);
169+
170+ disk_map.at (-1 ).erase (std::remove (disk_map.at (-1 ).begin (), disk_map.at (-1 ).end (), first_empty_index + i), disk_map.at (-1 ).end ());
171+ }
172+
173+ for (const int & block_pos : block_positions) {
174+ disk_map.at (-1 ).push_back (block_pos);
175+ }
176+
177+ return block_index - 1 ;
178+ }
179+
180+ DiskMap cleanup_disk (const std::filesystem::path& input_file, bool file_move, bool print_iterations) {
181+ std::ifstream read_in (input_file, std::ios::in);
182+ std::string line;
183+
184+ std::getline (read_in, line);
185+
186+ DiskMap disk_map{get_disk_map (line)};
187+
188+ if (file_move) {
189+ auto max_block = std::max_element (disk_map.getMap ().begin (), disk_map.getMap ().end (), [](const auto & a, const auto & b){return a.first < b.first ;});
190+ if (max_block == disk_map.getMap ().end ()) {
191+ throw std::runtime_error (" Failed to get maximum block index" );
192+ }
193+
194+ std::optional<int > block_index = {max_block->first };
195+
196+ while (block_index.has_value ()) {
197+ block_index = disk_map.file_cleanup_iteration (block_index.value ());
198+ if (print_iterations) disk_map.print ();
199+ }
200+
201+ } else {
202+ while (!file_move && disk_map.block_cleanup_iteration ()) {
203+ if (print_iterations) disk_map.print ();
204+ }
205+ }
206+
207+ return disk_map;
208+ }
209+
210+ long long DiskMap::checksum () const {
211+ return std::accumulate (
212+ disk_map.begin (),
213+ disk_map.end (),
214+ 0LL ,
215+ [](const long long & total, const std::pair<int , std::deque<int >>& a) {
216+ // Do not include space which has index -1
217+ if (a.first < 0 ) return total;
218+
219+ return total + a.first * std::accumulate (
220+ a.second .begin (),
221+ a.second .end (),
222+ 0LL
223+ );
224+ }
225+ );
226+ }
227+ };
0 commit comments