While making the state immutable made it more visible what code was affecting the state, the end result is still multiple pieces of code directly affecting what is essentially a global state. Sure, a copy is passed from one function to the next, but there is still one 'current' state and everything is messing with it directly.
The real problem here is not the mutability of the state, it is the ownership of it. Who is responsible for keeping the state internally consistent ? In this code the answer is: no one.
To solve his problem, there needs to be a clear owner of the state, and that code should be the only code directly affecting the state and and be responsible for keeping the state internally consistent.
Wether this 'owner' is a collection of functions that operate on a global state in a language like C, or on a state passed in and returned, or an object in an OO language, or whatever. Doesn't really matter.
For example. Moving the character and collision detection should not be two separate function that affect the state but that can be called separately (or in the wrong order) and keep the system in an incorrect state. Only the code responsible for modifying the state should do so, and it should guarantee to leave it in a correct state on returning. Moving without collision detection can leave the system in an incorrect state and thus should not even be a function that exists.
When designing a system this is always something I keep in the back of my head: who is responsible for what ? Once you have that clear, things become much easier.
The real problem here is not the mutability of the state, it is the ownership of it. Who is responsible for keeping the state internally consistent ? In this code the answer is: no one.
To solve his problem, there needs to be a clear owner of the state, and that code should be the only code directly affecting the state and and be responsible for keeping the state internally consistent.
Wether this 'owner' is a collection of functions that operate on a global state in a language like C, or on a state passed in and returned, or an object in an OO language, or whatever. Doesn't really matter.
For example. Moving the character and collision detection should not be two separate function that affect the state but that can be called separately (or in the wrong order) and keep the system in an incorrect state. Only the code responsible for modifying the state should do so, and it should guarantee to leave it in a correct state on returning. Moving without collision detection can leave the system in an incorrect state and thus should not even be a function that exists.
When designing a system this is always something I keep in the back of my head: who is responsible for what ? Once you have that clear, things become much easier.