2. Porovnání (Newtonův fraktál)
Porovnání jednotlivých řešení po měkkém deadline druhého úkolu.
using CSV, DataFrames"""
record_rank!(data_frame, column)
Pomocná hodnotící funkce přidělující body do sloupce `score` tabulky
`data_frame` podle pořadí ve sloupci `column` (seřazeném).
"""
function record_rank!(data_frame::DataFrame, column::Symbol)
rank = 1
value = first(data_frame[!, column])
for row in eachrow(data_frame)
if ismissing(row[column])
row[:score] += 42
continue
elseif ismissing(value)
value = row[column]
elseif row[column] != value
value = row[column]
rank += 1
end
row[:score] += rank
end
end;Načtení dat, smazání username a přidání "skórovacího" sloupce a výpis základních statistik datasetu.
df = CSV.read("benchmark02.csv", DataFrame)
df[!, "score"] .= 0
select!(df, Not(:username))
describe(df)| Row | variable | mean | min | median | max | nmissing | eltype |
|---|---|---|---|---|---|---|---|
| Symbol | Union… | Any | Union… | Any | Int64 | Type | |
| 1 | nick | Aeloria of the Seven Sigils | kalvotom | 0 | String | ||
| 2 | code | 107.36 | 24 | 110.0 | 190 | 2 | Union{Missing, Int64} |
| 3 | comment | 36.16 | 6 | 28.0 | 131 | 2 | Union{Missing, Int64} |
| 4 | correctness | 0.826923 | false | 1.0 | true | 0 | Bool |
| 5 | newton_t | 3379.03 | 2067.22 | 3115.88 | 17022.0 | 8 | Union{Missing, Float64} |
| 6 | newton_m | 374.545 | 0 | 0.0 | 16480 | 8 | Union{Missing, Int64} |
| 7 | constructor_t | 1.7945e6 | 261.0 | 2.45268e6 | 4.42194e6 | 8 | Union{Missing, Float64} |
| 8 | constructor_m | 3.23639e7 | 32000296 | 3.20003e7 | 48000384 | 8 | Union{Missing, Int64} |
| 9 | compute_t | 9.39814e8 | 1.28338e8 | 8.46475e8 | 4.56455e9 | 9 | Union{Missing, Float64} |
| 10 | compute_m | 2.63132e8 | 1056 | 1.28e8 | 3973726928 | 9 | Union{Missing, Int64} |
| 11 | score | 0.0 | 0 | 0.0 | 0 | 0 | Int64 |
Do měření započítávám pouze odevzdání, která počítají správné výsledky na jednoduchém případu.
Dále mezi hodnocené zařazuji i své odevzdání (nick kalvotom), kód vygenerovaný pomocí ChatGPT (nick chatgpt).
df = df[df[:, :correctness], :];Parametry kódu
Počet řádek kódu. Čím méně, tím lépe, což ale vždy neplatí, že. Měření pomocí nástroje cloc.
sort!(df, :code)
record_rank!(df, :code)
show(df[!,[:nick, :code, :score]], allrows=true)43×3 DataFrame Row │ nick code score │ String Int64? Int64 ─────┼────────────────────────────────────────────────── 1 │ Orlien Silverchant 73 1 2 │ Zytherin the Forgotten 75 2 3 │ Ilyrien Dawnpetal 81 3 4 │ Rhovar Mistborn 86 4 5 │ Galdur the Wise 93 5 6 │ Erenwyn the Whispering 93 5 7 │ Seraphyne Windwhisper 95 6 8 │ Sareth the Verdant 95 6 9 │ Elithar Dawnseeker 96 7 10 │ Talvorn Runebreaker 96 7 11 │ Cyralis Moonbinder 97 8 12 │ Caelwyn of the Crystal Spire 98 9 13 │ Vessryn Bloodsong 99 10 14 │ kalvotom 100 11 15 │ Tirvian Shadowmantle 100 11 16 │ Dravok the Gloomed 101 12 17 │ Maerilith Stormweaver 103 13 18 │ Maranelle Frostpetal 110 14 19 │ Althira Starweaver 110 14 20 │ Eldaric Runehand 111 15 21 │ Talmek the Shattered 115 16 22 │ Vyrion the Eternal 116 17 23 │ Korvath the Obsidian 120 18 24 │ Mirelda Starbloom 122 19 25 │ Faelora Nightbloom 122 19 26 │ Yseline Frostwhisper 122 19 27 │ Kerron the Unseen 123 20 28 │ Selvethra of the Whispering Woods 124 21 29 │ Aeloria of the Seven Sigils 127 22 30 │ Xandor the Arcane 127 22 31 │ Thalendir Brightflame 129 23 32 │ Zerathorn the Pale 129 23 33 │ Korthen Deepflame 134 24 34 │ Velmaris Lightbinder 134 24 35 │ Thamior Embercloak 136 25 36 │ Lunara Dreambinder 141 26 37 │ Sylthara the Veiled 142 27 38 │ Nimriel the Golden 143 28 39 │ chatgpt 148 29 40 │ Orren Emberforge 154 30 41 │ Velindra the Azure 162 31 42 │ Bramor the Tempest 179 32 43 │ Eldwyn of the Shimmering Vale 190 33
Počet řádek komentářů (včetně docstringů). Zde naopak čím více, tím lépe.
sort!(df, :comment, rev=true)
record_rank!(df, :comment)
show(df[!,[:nick, :comment, :score]], allrows=true)43×3 DataFrame Row │ nick comment score │ String Int64? Int64 ─────┼─────────────────────────────────────────────────── 1 │ Bramor the Tempest 131 33 2 │ Eldaric Runehand 118 17 3 │ Eldwyn of the Shimmering Vale 118 35 4 │ Faelora Nightbloom 87 22 5 │ Kerron the Unseen 64 24 6 │ Orlien Silverchant 60 6 7 │ Maerilith Stormweaver 58 19 8 │ Aeloria of the Seven Sigils 49 29 9 │ Talmek the Shattered 47 24 10 │ Xandor the Arcane 45 31 11 │ Korthen Deepflame 42 34 12 │ Seraphyne Windwhisper 41 17 13 │ Velmaris Lightbinder 41 35 14 │ Nimriel the Golden 41 39 15 │ chatgpt 41 40 16 │ Rhovar Mistborn 40 16 17 │ Thamior Embercloak 40 37 18 │ Zerathorn the Pale 38 36 19 │ Lunara Dreambinder 36 40 20 │ Korvath the Obsidian 32 33 21 │ Vessryn Bloodsong 31 26 22 │ kalvotom 31 27 23 │ Cyralis Moonbinder 29 25 24 │ Sylthara the Veiled 28 45 25 │ Althira Starweaver 27 33 26 │ Selvethra of the Whispering Woods 27 40 27 │ Thalendir Brightflame 26 43 28 │ Caelwyn of the Crystal Spire 25 30 29 │ Orren Emberforge 24 52 30 │ Maranelle Frostpetal 23 37 31 │ Yseline Frostwhisper 23 42 32 │ Tirvian Shadowmantle 21 35 33 │ Galdur the Wise 20 30 34 │ Elithar Dawnseeker 20 32 35 │ Talvorn Runebreaker 20 32 36 │ Zytherin the Forgotten 19 28 37 │ Ilyrien Dawnpetal 18 30 38 │ Erenwyn the Whispering 18 32 39 │ Dravok the Gloomed 18 39 40 │ Vyrion the Eternal 18 44 41 │ Velindra the Azure 15 59 42 │ Sareth the Verdant 14 35 43 │ Mirelda Starbloom 14 48
Metoda newton
Test efektivity a paměťové náročnosti metody newton, která provádí čistě iterace. Rychlost (čas, první tabulka) je přepočtena relativně vzhledem k nejrychlejšímu řešení. Druhá tabulka není normalizována, ani to nejde, vzhledem k nulovým hodnotám.
sort!(df, :newton_t)
df[!, :newton_t] /= df[1, :newton_t]
record_rank!(df, :newton_t)
show(df[!,[:nick, :newton_t, :score]], allrows=true)43×3 DataFrame Row │ nick newton_t score │ String Float64 Int64 ─────┼──────────────────────────────────────────────────── 1 │ kalvotom 1.0 28 2 │ Mirelda Starbloom 1.09798 50 3 │ Orlien Silverchant 1.12819 9 4 │ Aeloria of the Seven Sigils 1.14324 33 5 │ Galdur the Wise 1.3355 35 6 │ Maerilith Stormweaver 1.36297 25 7 │ Velindra the Azure 1.39855 66 8 │ Eldaric Runehand 1.39957 25 9 │ Lunara Dreambinder 1.40769 49 10 │ Kerron the Unseen 1.4136 34 11 │ Maranelle Frostpetal 1.42596 48 12 │ Faelora Nightbloom 1.45289 34 13 │ Ilyrien Dawnpetal 1.45988 43 14 │ Thalendir Brightflame 1.49155 57 15 │ Elithar Dawnseeker 1.49397 47 16 │ Talvorn Runebreaker 1.49397 47 17 │ Caelwyn of the Crystal Spire 1.49458 46 18 │ Xandor the Arcane 1.49512 48 19 │ Seraphyne Windwhisper 1.49579 35 20 │ Vessryn Bloodsong 1.49936 45 21 │ Selvethra of the Whispering Woods 1.50123 60 22 │ Bramor the Tempest 1.50667 54 23 │ Tirvian Shadowmantle 1.50788 57 24 │ Yseline Frostwhisper 1.5279 65 25 │ Thamior Embercloak 1.52971 61 26 │ Zytherin the Forgotten 1.53818 53 27 │ Rhovar Mistborn 1.53999 42 28 │ Erenwyn the Whispering 1.54241 59 29 │ Cyralis Moonbinder 1.54362 53 30 │ Sylthara the Veiled 1.5467 74 31 │ Velmaris Lightbinder 1.56847 65 32 │ Korvath the Obsidian 1.59332 64 33 │ Korthen Deepflame 1.59514 66 34 │ Nimriel the Golden 1.59635 72 35 │ Althira Starweaver 1.59997 67 36 │ Vyrion the Eternal 1.60179 79 37 │ Orren Emberforge 1.60239 88 38 │ Sareth the Verdant 1.603 72 39 │ Dravok the Gloomed 1.60421 77 40 │ chatgpt 1.60481 79 41 │ Zerathorn the Pale 1.66056 76 42 │ Eldwyn of the Shimmering Vale 1.74721 76 43 │ Talmek the Shattered 8.23424 66
A pak paměť ve stejném experimentu, čím méně, tím lépe.
sort!(df, :newton_m)
record_rank!(df, :newton_m)
show(df[!, [:nick, :newton_m, :score]], allrows=true)43×3 DataFrame Row │ nick newton_m score │ String Int64? Int64 ─────┼──────────────────────────────────────────────────── 1 │ kalvotom 0 29 2 │ Mirelda Starbloom 0 51 3 │ Orlien Silverchant 0 10 4 │ Aeloria of the Seven Sigils 0 34 5 │ Galdur the Wise 0 36 6 │ Maerilith Stormweaver 0 26 7 │ Velindra the Azure 0 67 8 │ Eldaric Runehand 0 26 9 │ Lunara Dreambinder 0 50 10 │ Kerron the Unseen 0 35 11 │ Maranelle Frostpetal 0 49 12 │ Faelora Nightbloom 0 35 13 │ Ilyrien Dawnpetal 0 44 14 │ Thalendir Brightflame 0 58 15 │ Elithar Dawnseeker 0 48 16 │ Talvorn Runebreaker 0 48 17 │ Caelwyn of the Crystal Spire 0 47 18 │ Xandor the Arcane 0 49 19 │ Seraphyne Windwhisper 0 36 20 │ Vessryn Bloodsong 0 46 21 │ Selvethra of the Whispering Woods 0 61 22 │ Bramor the Tempest 0 55 23 │ Tirvian Shadowmantle 0 58 24 │ Yseline Frostwhisper 0 66 25 │ Thamior Embercloak 0 62 26 │ Zytherin the Forgotten 0 54 27 │ Rhovar Mistborn 0 43 28 │ Erenwyn the Whispering 0 60 29 │ Cyralis Moonbinder 0 54 30 │ Sylthara the Veiled 0 75 31 │ Velmaris Lightbinder 0 66 32 │ Korvath the Obsidian 0 65 33 │ Korthen Deepflame 0 67 34 │ Nimriel the Golden 0 73 35 │ Althira Starweaver 0 68 36 │ Vyrion the Eternal 0 80 37 │ Orren Emberforge 0 89 38 │ Sareth the Verdant 0 73 39 │ Dravok the Gloomed 0 78 40 │ chatgpt 0 80 41 │ Zerathorn the Pale 0 77 42 │ Eldwyn of the Shimmering Vale 0 77 43 │ Talmek the Shattered 16480 68
Konstruktor Newton
Test konstruktoru typu Newton z pohledu časové a paměťové náročnosti. Jak vidíte, i zde jde velký prostor pro zamyšlení.
sort!(df, :constructor_t)
df[!, :constructor_t] /= df[1, :constructor_t]
record_rank!(df, :constructor_t)
show(df[!,[:nick, :constructor_t, :score]], allrows=true)43×3 DataFrame Row │ nick constructor_t score │ String Float64 Int64 ─────┼───────────────────────────────────────────────────────── 1 │ kalvotom 1.0 30 2 │ Velmaris Lightbinder 1.03831 68 3 │ Tirvian Shadowmantle 1.0728 61 4 │ Faelora Nightbloom 1.15326 39 5 │ Ilyrien Dawnpetal 1.15326 48 6 │ Korthen Deepflame 1.15326 71 7 │ Orlien Silverchant 1.19157 15 8 │ Erenwyn the Whispering 1.19157 65 9 │ Thalendir Brightflame 1.30651 64 10 │ Zerathorn the Pale 1901.87 84 11 │ Eldaric Runehand 2285.59 34 12 │ Althira Starweaver 2967.17 77 13 │ Bramor the Tempest 3734.38 65 14 │ Aeloria of the Seven Sigils 3746.92 45 15 │ Vyrion the Eternal 3758.78 92 16 │ Orren Emberforge 3786.03 102 17 │ Dravok the Gloomed 4018.39 92 18 │ Mirelda Starbloom 4120.34 66 19 │ Caelwyn of the Crystal Spire 4247.87 63 20 │ Selvethra of the Whispering Woods 4587.85 78 21 │ Elithar Dawnseeker 9317.45 66 22 │ Seraphyne Windwhisper 9390.59 55 23 │ Yseline Frostwhisper 9403.91 86 24 │ Lunara Dreambinder 9492.24 71 25 │ Sareth the Verdant 9527.4 95 26 │ Xandor the Arcane 9776.11 72 27 │ Sylthara the Veiled 9824.25 99 28 │ Korvath the Obsidian 9969.96 90 29 │ Nimriel the Golden 9985.16 99 30 │ Velindra the Azure 10006.8 94 31 │ Vessryn Bloodsong 10149.5 74 32 │ Galdur the Wise 10170.6 65 33 │ Maranelle Frostpetal 10180.2 79 34 │ Kerron the Unseen 10441.1 66 35 │ Talmek the Shattered 10473.7 100 36 │ Cyralis Moonbinder 10528.0 87 37 │ Rhovar Mistborn 10631.0 77 38 │ Thamior Embercloak 10871.4 97 39 │ Maerilith Stormweaver 11435.2 62 40 │ Eldwyn of the Shimmering Vale 13088.7 114 41 │ Zytherin the Forgotten 14908.4 92 42 │ chatgpt 16371.0 119 43 │ Talvorn Runebreaker 16942.3 88
sort!(df, :constructor_m)
record_rank!(df, :constructor_m)
show(df[!, [:nick, :constructor_m, :score]], allrows=true)43×3 DataFrame Row │ nick constructor_m score │ String Int64? Int64 ─────┼───────────────────────────────────────────────────────── 1 │ kalvotom 32000296 31 2 │ Velmaris Lightbinder 32000296 69 3 │ Tirvian Shadowmantle 32000296 62 4 │ Faelora Nightbloom 32000296 40 5 │ Ilyrien Dawnpetal 32000296 49 6 │ Korthen Deepflame 32000296 72 7 │ Orlien Silverchant 32000296 16 8 │ Erenwyn the Whispering 32000296 66 9 │ Zerathorn the Pale 32000296 85 10 │ Eldaric Runehand 32000296 35 11 │ Althira Starweaver 32000296 78 12 │ Bramor the Tempest 32000296 66 13 │ Aeloria of the Seven Sigils 32000296 46 14 │ Vyrion the Eternal 32000296 93 15 │ Orren Emberforge 32000296 103 16 │ Dravok the Gloomed 32000296 93 17 │ Mirelda Starbloom 32000296 67 18 │ Caelwyn of the Crystal Spire 32000296 64 19 │ Selvethra of the Whispering Woods 32000296 79 20 │ Elithar Dawnseeker 32000296 67 21 │ Seraphyne Windwhisper 32000296 56 22 │ Yseline Frostwhisper 32000296 87 23 │ Lunara Dreambinder 32000296 72 24 │ Sareth the Verdant 32000296 96 25 │ Xandor the Arcane 32000296 73 26 │ Sylthara the Veiled 32000296 100 27 │ Korvath the Obsidian 32000296 91 28 │ Nimriel the Golden 32000296 100 29 │ Velindra the Azure 32000296 95 30 │ Vessryn Bloodsong 32000296 75 31 │ Galdur the Wise 32000296 66 32 │ Maranelle Frostpetal 32000296 80 33 │ Talmek the Shattered 32000296 101 34 │ Cyralis Moonbinder 32000296 88 35 │ Rhovar Mistborn 32000296 78 36 │ Thamior Embercloak 32000296 98 37 │ Maerilith Stormweaver 32000296 63 38 │ chatgpt 32000296 120 39 │ Talvorn Runebreaker 32000296 89 40 │ Thalendir Brightflame 32000360 66 41 │ Kerron the Unseen 32000408 69 42 │ Eldwyn of the Shimmering Vale 32000424 118 43 │ Zytherin the Forgotten 48000384 97
Metoda compute!
Test metody provádějící samotný výpočet a následné přiřazení výsledků k nalezeným kořenům. Opět testuji časovou a paměťovou náročnost.
sort!(df, :compute_t)
df[!, :compute_t] /= df[1, :compute_t]
record_rank!(df, :compute_t)
show(df[!,[:nick, :compute_t, :score]], allrows=true)43×3 DataFrame Row │ nick compute_t score │ String Float64? Int64 ─────┼───────────────────────────────────────────────────────── 1 │ Maerilith Stormweaver 1.0 64 2 │ Thamior Embercloak 3.46445 100 3 │ kalvotom 4.48503 34 4 │ Mirelda Starbloom 4.84819 71 5 │ Eldaric Runehand 5.61979 40 6 │ Aeloria of the Seven Sigils 5.77015 52 7 │ Selvethra of the Whispering Woods 5.81756 86 8 │ Lunara Dreambinder 6.03232 80 9 │ Galdur the Wise 6.1175 75 10 │ Maranelle Frostpetal 6.12431 90 11 │ Velindra the Azure 6.12815 106 12 │ Orlien Silverchant 6.18021 28 13 │ Kerron the Unseen 6.22315 82 14 │ Ilyrien Dawnpetal 6.31753 63 15 │ Faelora Nightbloom 6.3815 55 16 │ Elithar Dawnseeker 6.38935 83 17 │ Yseline Frostwhisper 6.47549 104 18 │ Bramor the Tempest 6.48255 84 19 │ Xandor the Arcane 6.48907 92 20 │ Dravok the Gloomed 6.58741 113 21 │ Tirvian Shadowmantle 6.59568 83 22 │ Sylthara the Veiled 6.63632 122 23 │ Velmaris Lightbinder 6.64143 92 24 │ Vyrion the Eternal 6.75217 117 25 │ Cyralis Moonbinder 6.82806 113 26 │ Sareth the Verdant 6.84734 122 27 │ Korvath the Obsidian 6.88628 118 28 │ Caelwyn of the Crystal Spire 7.15516 92 29 │ Nimriel the Golden 7.32284 129 30 │ Zerathorn the Pale 7.41458 115 31 │ Thalendir Brightflame 7.51646 97 32 │ Erenwyn the Whispering 7.55022 98 33 │ Zytherin the Forgotten 7.74482 130 34 │ Rhovar Mistborn 7.85948 112 35 │ Korthen Deepflame 7.95374 107 36 │ Orren Emberforge 7.98812 139 37 │ Eldwyn of the Shimmering Vale 7.99339 155 38 │ Althira Starweaver 8.5542 116 39 │ Talvorn Runebreaker 8.70592 128 40 │ Seraphyne Windwhisper 9.15685 96 41 │ chatgpt 9.84096 161 42 │ Talmek the Shattered 35.5667 143 43 │ Vessryn Bloodsong missing 117
A pak paměť, čím méně, tím lépe.
sort!(df, :compute_m)
record_rank!(df, :compute_m)
show(df[!, [:nick, :compute_m, :score]], allrows=true)43×3 DataFrame Row │ nick compute_m score │ String Int64? Int64 ─────┼────────────────────────────────────────────────────── 1 │ Selvethra of the Whispering Woods 1056 87 2 │ Eldaric Runehand 96000000 42 3 │ Dravok the Gloomed 96000000 115 4 │ Eldwyn of the Shimmering Vale 96000000 157 5 │ Maerilith Stormweaver 128000000 67 6 │ kalvotom 128000000 37 7 │ Mirelda Starbloom 128000000 74 8 │ Lunara Dreambinder 128000000 83 9 │ Galdur the Wise 128000000 78 10 │ Maranelle Frostpetal 128000000 93 11 │ Kerron the Unseen 128000000 85 12 │ Ilyrien Dawnpetal 128000000 66 13 │ Faelora Nightbloom 128000000 58 14 │ Elithar Dawnseeker 128000000 86 15 │ Yseline Frostwhisper 128000000 107 16 │ Bramor the Tempest 128000000 87 17 │ Xandor the Arcane 128000000 95 18 │ Tirvian Shadowmantle 128000000 86 19 │ Sylthara the Veiled 128000000 125 20 │ Velmaris Lightbinder 128000000 95 21 │ Vyrion the Eternal 128000000 120 22 │ Cyralis Moonbinder 128000000 116 23 │ Sareth the Verdant 128000000 125 24 │ Korvath the Obsidian 128000000 121 25 │ Zerathorn the Pale 128000000 118 26 │ Velindra the Azure 128016144 110 27 │ Caelwyn of the Crystal Spire 159649232 97 28 │ Nimriel the Golden 160000624 135 29 │ Aeloria of the Seven Sigils 176000000 59 30 │ Orlien Silverchant 176000000 35 31 │ Thalendir Brightflame 176000000 104 32 │ Erenwyn the Whispering 176000000 105 33 │ Zytherin the Forgotten 176000000 137 34 │ Rhovar Mistborn 176000000 119 35 │ chatgpt 224000000 169 36 │ Thamior Embercloak 256000576 109 37 │ Korthen Deepflame 391497760 117 38 │ Orren Emberforge 407497760 150 39 │ Althira Starweaver 407497760 127 40 │ Talvorn Runebreaker 407497760 139 41 │ Seraphyne Windwhisper 639296000 108 42 │ Talmek the Shattered 3973726928 156 43 │ Vessryn Bloodsong missing 159
Závěrečná tabulka
Zaznamenávali jsme v každé kategorii (sdílené) pořadí. Čím nižší hodnota, tím lepší výsledek.
sort!(df, :score)
show(df[!,[:nick, :score]], allrows=true)43×2 DataFrame Row │ nick score │ String Int64 ─────┼────────────────────────────────────────── 1 │ Orlien Silverchant 35 2 │ kalvotom 37 3 │ Eldaric Runehand 42 4 │ Faelora Nightbloom 58 5 │ Aeloria of the Seven Sigils 59 6 │ Ilyrien Dawnpetal 66 7 │ Maerilith Stormweaver 67 8 │ Mirelda Starbloom 74 9 │ Galdur the Wise 78 10 │ Lunara Dreambinder 83 11 │ Kerron the Unseen 85 12 │ Elithar Dawnseeker 86 13 │ Tirvian Shadowmantle 86 14 │ Selvethra of the Whispering Woods 87 15 │ Bramor the Tempest 87 16 │ Maranelle Frostpetal 93 17 │ Xandor the Arcane 95 18 │ Velmaris Lightbinder 95 19 │ Caelwyn of the Crystal Spire 97 20 │ Thalendir Brightflame 104 21 │ Erenwyn the Whispering 105 22 │ Yseline Frostwhisper 107 23 │ Seraphyne Windwhisper 108 24 │ Thamior Embercloak 109 25 │ Velindra the Azure 110 26 │ Dravok the Gloomed 115 27 │ Cyralis Moonbinder 116 28 │ Korthen Deepflame 117 29 │ Zerathorn the Pale 118 30 │ Rhovar Mistborn 119 31 │ Vyrion the Eternal 120 32 │ Korvath the Obsidian 121 33 │ Sylthara the Veiled 125 34 │ Sareth the Verdant 125 35 │ Althira Starweaver 127 36 │ Nimriel the Golden 135 37 │ Zytherin the Forgotten 137 38 │ Talvorn Runebreaker 139 39 │ Orren Emberforge 150 40 │ Talmek the Shattered 156 41 │ Eldwyn of the Shimmering Vale 157 42 │ Vessryn Bloodsong 159 43 │ chatgpt 169