Refactoring Conway's Game of Life | ArjanCodes Code Roast

  Рет қаралды 28,570

ArjanCodes

ArjanCodes

Күн бұрын

In this video, I'll refactor 'Conway's Game of Life', a submission by Lupo. 'The Game of Life', often referred to simply as 'Life', is a cellular automaton devised by the British mathematician John Conway in the 1970s.
👷 Join the FREE Code Diagnosis Workshop to help you review code more effectively using my 3-Factor Diagnosis Framework: www.arjancodes.com/diagnosis
GitHub Repository: git.arjan.codes/2024/golroast
💻 ArjanCodes Blog: www.arjancodes.com/blog
✍🏻 Take a quiz on this topic: www.learntail.com/quiz/dxxrmf
Try Learntail for FREE ➡️ www.learntail.com/
🎓 Courses:
The Software Designer Mindset: www.arjancodes.com/mindset
The Software Architect Mindset: Pre-register now! www.arjancodes.com/architect
Next Level Python: Become a Python Expert: www.arjancodes.com/next-level...
The 30-Day Design Challenge: www.arjancodes.com/30ddc
🛒 GEAR & RECOMMENDED BOOKS: kit.co/arjancodes.
👍 If you enjoyed this content, give this video a like. If you want to watch more of my upcoming videos, consider subscribing to my channel!
Social channels:
💬 Discord: discord.arjan.codes
🐦Twitter: / arjancodes
🌍LinkedIn: / arjancodes
🕵Facebook: / arjancodes
📱Instagram: / arjancodes
♪ Tiktok: / arjancodes
👀 Code reviewers:
- Yoriz
- Ryan Laursen
- Dale Hagglund
🎥 Video edited by Mark Bacskai: / bacskaimark
🔖 Chapters:
0:00 Intro
1:16 Review
9:18 Refactoring
31:12 Final thoughts
#arjancodes #softwaredesign #python
DISCLAIMER - The links in this description might be affiliate links. If you purchase a product or service through one of those links, I may receive a small commission. There is no additional charge to you. Thanks for supporting my channel so I can continue to provide you with free content each week!

Пікірлер: 136
@ArjanCodes
@ArjanCodes 4 ай бұрын
👷 Join the FREE Code Diagnosis Workshop to help you review code more effectively using my 3-Factor Diagnosis Framework: www.arjancodes.com/diagnosis
@aryindra2931
@aryindra2931 4 ай бұрын
Please discuss the socketify framework from python which can beat go fiber and go gin, now socketify is called the fastest framework
@georgehammond867
@georgehammond867 4 ай бұрын
Are you using Mac and what kind of keyboard ??
@dragonfly-7
@dragonfly-7 4 ай бұрын
I'm not sure if I had been the only one waiting desperately for a code roast ... 🤗 ... So: Arjan at least made me happy ... 🤓
@ArjanCodes
@ArjanCodes 4 ай бұрын
Ahah! I'm glad you enjoyed the roast. :)
@thearjunadhikari
@thearjunadhikari 4 ай бұрын
I just wanted to take a moment to express my gratitude for the content you've been sharing on your KZbin channel, especially your insightful roasting and refactoring of code. Your ability to dissect complex code and explain how it can be improved is not only educational but also incredibly engaging. Your videos have not only helped me understand better coding practices but also inspired me to take a more critical and thoughtful approach to my own code. The way you break down each aspect of the code and suggest practical improvements is tremendously helpful.
@ArjanCodes
@ArjanCodes 4 ай бұрын
I'm very happy to hear that my videos have been helpful! Thank you for these kind words. :)
@ScriptRaccoon
@ScriptRaccoon 4 ай бұрын
The idea of turning the grid class into an iterable is just👌
@Michallote
@Michallote 4 ай бұрын
Im baffled too, this would have solved me sooo much trouble in the past ugh
@carlesmolins3269
@carlesmolins3269 4 ай бұрын
Isn’t it a bit weird that the visualizers are the ones responsibles for actually running the game? I would also have loved to see the implementation of the strategy pattern for the visualizers. As always, super interesting!
@kennyostrom3098
@kennyostrom3098 4 ай бұрын
perhaps the visualizer should just take a Grid
@CottidaeSEA
@CottidaeSEA 4 ай бұрын
I agree, I think the visualizers should be passed the grid and the visualizer should be passed to the Game class in order for the Game logic to have rendering. Would make a lot more sense.
@EquiliMario
@EquiliMario 4 ай бұрын
If you want to replace something that occurs multiple times in the code, you can use CTRL+D to select them. If you then type it replaces all of those instances. It's like find & replace but a bit quicker. Avoids the use of many mouse movements
@tommacnamara5430
@tommacnamara5430 3 ай бұрын
I'm not sure if Ctrl+D works the same but if you use F2 when hightlighting a variable/class/constant name it will change all references to that variable in all files.
@redpepper74
@redpepper74 2 ай бұрын
@@tommacnamara5430That definitely has its uses, but the CTRL-D command is really nice when you need to replace a few duplicate expressions (it allows you to match an arbitrary string, such as `config['generations']`)
@TaurusWD
@TaurusWD 4 ай бұрын
Not a VSCode user, but in PyCharm the IDE has you specify where your source files are located and where your test files are located. When things don't comply with the IDE configuration it tends to throw errors even though the actual local configuration works. I suspect that is what is occurring. While the before and after directories are perfectly valid from the scope within the directory, the IDE is looking from the parent directory and saying "I don't see something like from after.grid import Grid" even though it's not needed because within the after directory everything has relative imports occurring.
@dhiahmila9549
@dhiahmila9549 4 ай бұрын
indeed in pycharm you can mark directories as source files, so it adds them automatically to PYTHON_PATH list Alternatively, Installing you package with "pip install -e ." would do the trick ( if other script files are added after that, reinstalling usually does the trick )
@mebewoke206
@mebewoke206 4 ай бұрын
Afs ds
@Impatient_Ape
@Impatient_Ape 4 ай бұрын
A trick I've used in cellular automata simulations is to create a "dummy" perimeter, and either leave it blank or copy the opposite row/column (for a toroidal geometry). While the copying takes a little time, it ends up saving more time in the long run because you never have to do if-else checks for the boundary.
@dalehagglund
@dalehagglund 4 ай бұрын
For a toroidal (ie, wraparound) topology, I might just do all indexing modulo the grid bounds. But I agree completely that extending the perimeter to avoid special cases is very valuable.
@landsgevaer
@landsgevaer 4 ай бұрын
An alternative would be to copy the 2nd (or 3rd) column to the 1st, etc., to get a "mirrored tiling" geometry. (Or use a min/max operation to clip the index to the represented bounds.) Adding zeros I would personally avoid since that makes the model no longer correspond to at least some infinitely extended world that could occur. So you might introduce behaviour that the rules do not allow, in principle.
@Impatient_Ape
@Impatient_Ape 4 ай бұрын
@@landsgevaer I've never liked using fixed edges in CA simulations, and have always opted for the toroidal gemoetry since it gives us many more opportunities to witness interesting behavior. The dummy perimeter is an easy way to obtain this. Back in the day, when I ran a CA on a Beowulf cluster, I used the dummy rows/columns things again with MPI calls to copy the perimeter cells from one CA grid running on one processor to the dummy rows/columns on the CA grids running on other processors.
@Impatient_Ape
@Impatient_Ape 4 ай бұрын
@@dalehagglund I tried the modulus thing too early on, but I was doing CA sims on an old 386 PC (this was in 1992) using the Borland Turbo C and VGA graphics, and it was wayyy too slow for big grids. I found the dummy rows/columns method was significantly faster. But today, we are so spoiled for processor speed and memory space that you can see nice results no matter how inefficient the approach.
@aliriano15
@aliriano15 4 ай бұрын
Hey Arjan! Loved the vid. One thing I learned recently is that pytest and unittest are compatible with each other, so you can create the tests with the unittest framework and run them in pytest. I find this particularly useful because I think the classes in unittest generally encapsule in a cleaner way some conditions to run the tests (set up, fixtures), while the simplicity of pytest and the ability to generate coverage reports is so convenient
@ruslangabitov5202
@ruslangabitov5202 4 ай бұрын
We wrote this LifeCells 7 years ago when I teach my nephew software development. We used PyGame with SDL for displaying the cells state. Since it was his first project on python we didn't use classes. The task itself we took from Etudes for Programmers by Charles Whetherell .
@teprox7690
@teprox7690 4 ай бұрын
Your Code Rosts are really beautiful and very valuable, there should be a lot more of them! It's the content I enjoy and learn the most from you. 😍😍😍
@ArjanCodes
@ArjanCodes 4 ай бұрын
Thank you so much for the kind words! I'm glad you enjoy this type of content. :)
@graveetone
@graveetone Ай бұрын
Currently I'm working on refactoring a really messy code with 1.2K lines so this video is both helpful and fun for me. Thank you for that!
@ArjanCodes
@ArjanCodes Ай бұрын
I'm glad to hear that my video is being helpful! Good luck getting through those 1.2K lines.
@kobajgenti2373
@kobajgenti2373 4 ай бұрын
most polite roast I have even seen.
@ShanilPanara
@ShanilPanara 4 ай бұрын
Always learn something from these roasts! Thanks Arjan!
@ArjanCodes
@ArjanCodes 4 ай бұрын
I'm glad this content has been useful to you, Shanil!
@lexta13
@lexta13 4 ай бұрын
If you get import errors in VSCode it’s often just that the language server did not catch renamed or new files. Restarting the language server from the command palette will often fix it.
@annahri
@annahri 4 ай бұрын
Need more of this!
@xnick_uy
@xnick_uy 4 ай бұрын
Great content, as expected! 😀 At 29:16 I was thinking that instead of having OUTPUT_TYPE as a string constant upon wich the visualizer to use is chosen (lines 25-28), it is posible to have a VISUALIZER constant defined as a reference to the desired function (Callable) to use. Thus, instead of an if-else statemente we could replace all that by VISUALIZER(game, GENERATIONS, SLEEP_TIME) Would this be a good, bad or irrelevant idea?
@johnnyz1206
@johnnyz1206 4 ай бұрын
Hi Arjan, your video really helps me a lot, thank you for making such great content. By the way, I found cmd-d is really useful when you try to modify multiple occurrences of the same text in VS code.
@arturabizgeldin9890
@arturabizgeldin9890 4 ай бұрын
That was awesome! Thank you for your work, it was kind of spring cleaning in a house :D Few questions: 1. After refactoring in those tests for game we can assert by comparing with raw_grid property instead of grid.grid, right? 2. What do you think about syntax like the following suggestion for Grid constructor method: self.grid = init_state OR [[0] * self.cols for ... ? 3. And what do you think about using match keyword as a visualizing selector? 4. And I would also suggest to add option to provide initially alive cells via const or config file whereas the other consts can be derived from env vars or arguments
@Shtyke
@Shtyke 4 ай бұрын
The path issue is happening because your top level workspace is "recording". If you opened the IDE from just the "after" directory, you won't have these linting issues. You can edit your .vscode/settings file to add the "after" folder to the interpreter.
@antonzimin3999
@antonzimin3999 4 ай бұрын
Arjan, in vscode you can highlight one variable and then press "ctrl + d" or "command + d" on mac a few times and change all highlighted variables in this file at one time. It keeps your time free. And thanks for the helpful channel. With the best wishes )))
@Draggeta
@Draggeta 4 ай бұрын
If your LSP supports it, F2 is even better. It only changes the symbol in the correct contexts. It will rename I within one for loop, while leaving it alone in another loop for example. In some LSPs, you can even change names between files, like rename a class or a class property and it renames it everywhere it's used within your project.
@antonzimin3999
@antonzimin3999 4 ай бұрын
@@Draggeta I only told about what I saw. "VS Code" default settings give you this function from the box. @ArjanCodes pls give me your own opinion.
@Draggeta
@Draggeta 4 ай бұрын
@@antonzimin3999 that is a fair take. My suggestion doesn't work out of the box for most languages. It also only works for symbols, not random text in your code.
@laurenlewis4189
@laurenlewis4189 4 ай бұрын
On one hand, duck typing is a major reason why I picked up python as my first actual programming language. On the other, the type hints really do make it more readable and simplify any future docstrings. On the third hand, as an irate and non-tech-savvy friend once said: why are we still designing languages that are dependent on knowing how the hardware handles data? Isn't the whole point of abstraction so that we can just trust the implementation and make it easier on ourselves?
@martinuphoffs3
@martinuphoffs3 4 ай бұрын
Regarding the pylint error: this happens when using pylint and the workspace folder is not the init hook for imports. You can fix that by passing an init hook parameter to pylint. Personally, I have a .pylintrc file my .vscode folder (have that one all the time anyway) and specify the location of the rcfile in the workspace settings.json: "pylint.args": ["--rcfile=${workspaceFolder}/.vscode/.pylintrc"]. The .rcfile in turn contains the following line: init-hook='import sys; sys.path.append("path/to/top_level_python_dir")'. Meaning this is the directory with e.g. the main and the imported packages are in dirs inside that folder. Hope that helps.
@lucasmonteiro-lp1gy
@lucasmonteiro-lp1gy 4 ай бұрын
When you importing class manually, just reload window from vscode, it should works
@CS__2314
@CS__2314 4 ай бұрын
A new code roast! Let's grab our favorite beverage 🍷 and popcorn 🍿
@andreasbrey6277
@andreasbrey6277 4 ай бұрын
Ty for that tutorial showing basic, valid refactoring methods. My suggestion: It makes sense to give some sense to the audience, i.e. explain some motivational scenarios where refactoring actually makes sense vs. where you would not recommend it. From my experience about 50% of nasty code will remain 'black box' and untouched...
@jiillescas
@jiillescas 4 ай бұрын
Something I have issues with are exceptions, please create a video on how you should handle exceptions, when it makes sense to create your own, when you just catch everything. Good and bad practices.
@TimoRJensen
@TimoRJensen 4 ай бұрын
Good one! Years back, when I learned python I created exceptions for everything. Nowadays I very very rarely ever find the need to create one. It's only when I want to make type of error really explicit and want to handle certain cases in try except blocks. But that is almost always worst than correcting the root cause. Think of exceptions as GOTO-Statements an you will understand why. in 99%+ of the cases it's best to crash the program anyway with an exception. So it's not that important to make it its own type.
@AMusset
@AMusset 4 ай бұрын
Awesome video, you got a new sub out of it :) I'm curious to know which keyboard you use, it has a very particular sound. Is it Topre based ? Edit: I grabbed the code you modified since I'm new to python but on my computer the line "type Rule = Callable[[int, int], int | None]" general an error, I need to remove "type" for it to work. Any idea if there is a package missing ? I'm running Python 3.11.7 Thank you
@ArjanCodes
@ArjanCodes 4 ай бұрын
Glad you liked the video! The type syntax was introduced in version 3.12.
@AMusset
@AMusset 4 ай бұрын
@@ArjanCodes out of curiosity which are you using? It has a very peculiar sound.
@henrikholst7490
@henrikholst7490 4 ай бұрын
What keyboard do you use? Looks like a TKL? But which one?
@JDiel
@JDiel 4 ай бұрын
Hi Arjan, thanks for this video. Wouldn't it be simpler to represent a cell as a boolean (True represents alive)? In that case you rephrase return self.grid[row][col] == 1 if self.is_cell_in_bounds(row, col) else False as follows (just one example): return self.grid[row][col] if self.is_cell_in_bounds(row, col) else False I haven't worked it out completely, but I think there is a lot of simplification potential in this approach.
@mke7605
@mke7605 4 ай бұрын
@ArianCodes I have been looking at the Architect mindset course. When will it be available to subscribe?
@ArjanCodes
@ArjanCodes 4 ай бұрын
It will launch next Wednesday!
@houstonbova3136
@houstonbova3136 4 ай бұрын
I don’t know how to fix it with pylint. But I use Pyright and you can add [tool.pyright] executionEnvironment = [ { root = “src” } ] Where src is the root folder of the project. I find it works better to put all the code in a “src” or “app” folder. But I also prefer to use fully qualified paths over relative imports.
@marcelwitoschek7070
@marcelwitoschek7070 4 ай бұрын
Setting python.analysis.extraPaths in vscode settings to include the folder from where the main would run should fix the pylint. e.g "python.analysis.extraPaths": [ "after" ]
@ArjanCodes
@ArjanCodes 4 ай бұрын
Thanks! Actually, I’m currently experimenting with Ruff, which also solves the problem and is a lot faster than pylint 😎.
@houstonbova3136
@houstonbova3136 4 ай бұрын
I also use ruff but it doesn’t do type checking so I use Pyright in conjunction. I made another comment on how to fix this on a project basis using the pyproject file as well so it’s valid for all users without having to set Global’s in every editor for every project it just ships with it.
@lehela
@lehela 4 ай бұрын
Alternatively, you could tell Pylint to use the files' directory as working directory, i.e. "pylint.cwd":"${fileDirname}"
@vkit280
@vkit280 4 ай бұрын
Hi, Arjan! Can agree to most you have done there except making config constants. It was such a nice config to be moved to some config.json as per best practice when config is stored outside the code but you made it worse - just created consts and if I want for example different size board I have to play with consts in code instead of adjusting values in config file. Clearly don't agree with this. Also - even pytest is my personal preference too - I would not touch Unittests as that was working preference of author and could have stayed, but I obviously agree that pytest is better. Overall - great work! One more video I can suggest my students to watch to learn how to work with code!
@dbw16d
@dbw16d 4 ай бұрын
Nice Video, the only thing that bugs me is the return type of a rule function... I dont like [int | None], it is both a pain for type checking later on when the program grows, and I think obscures some of the characteristics of a birth/death rule.
@kennyostrom3098
@kennyostrom3098 4 ай бұрын
Rules have three responses: set alive (1), set dead (0), do nothing (None). He skipped the details of the game because it's pretty old, well known, and we can look it up.
@dbw16d
@dbw16d 4 ай бұрын
Yeah I know the rules… The outcome of a rule has two possibilities, the cell is alive or dead. So any rule can simply return 0 or 1… (note, you do need to know the cell state before the rule was applied, but you need that to apply the rule any ways so no problem) I did chuck together a commit with the rules cleaned up but KZbin won’t let me post the commit url…
@IsmeGenius
@IsmeGenius 4 ай бұрын
@@dbw16d Yes, the thing actually deserving a refactoring stayed in place.
@marcdelabarreraibardalet4754
@marcdelabarreraibardalet4754 4 ай бұрын
I guess it would involve more work, but the code creates a new grid every time. It would be better to have an atribute "active cells" in the grid and only check for those active cells and their neigbours, and update the grid every time.
@Geza_Molnar_
@Geza_Molnar_ 4 ай бұрын
I doubt if only active cells would be processed that would bring the same result. E.g. a none active cell may become an active cell when the rules are set like that, by my understanding. Illustration for such a rule: if zero neighbours are active of an inactive cell then an all inactive grid becomes all active in the next round.
@marcdelabarreraibardalet4754
@marcdelabarreraibardalet4754 4 ай бұрын
@@Geza_Molnar_ For sure, it's not enough. You have to check active cells AND their neigbours (to see if they become active). But not the entire grid, which seems a waste of computing time.
@kennyostrom3098
@kennyostrom3098 4 ай бұрын
You must create a new grid in order to properly execute the algorithm, because you have to apply the current state to all the cells, without changing the current state.
@Geza_Molnar_
@Geza_Molnar_ 4 ай бұрын
@@marcdelabarreraibardalet4754 How would your program handle this rule: if an inactive cell has only inactive neighbours it becomes active.
@marcdelabarreraibardalet4754
@marcdelabarreraibardalet4754 4 ай бұрын
@@kennyostrom3098 I see your point, but still looping and creating an entire new grid seems too much. The grid should have an "update" method that changes the active and inactive cells without creating a new grid. Possibly you need a buffer to store the initial state, but only the active cells and their neigbours.
@vladimirtchuiev2218
@vladimirtchuiev2218 4 ай бұрын
I would consider moving the configs into a separate yaml/json file and handle the rules via a filling a registry and calling the keys, but it might be more complicated to do it this way... Also, i dont like nested lists unless you need to store objects in them, i would prefer numpy arrays or even torch tensors if you need gpu computations for some reason, much more suited to handle grids IMO.
@Tekay37
@Tekay37 4 ай бұрын
24:40 using the 1 three times as a value in that test is not a good practice. If someone messes up row and col in the function, the test would still pass. Also, instead of directly accessing the internal grid value, one should use a function like is_alive() that checks for the value. This way, refactoring grid.grid into something else than a 2D list, the tests won't break.
@JavierRuizGonzalez
@JavierRuizGonzalez 4 ай бұрын
I enjoyed the refactoring but to check if a list my_list is not None must be done explicitly and not with "if my_list".
@Hamsters_Rage
@Hamsters_Rage 4 ай бұрын
unit tests refactoring is beautiful. no explanations, no error messages, but it is in functions instead of classes - big plus. game protocol class defined twice - for each visualizer removing config is also strange idea - config as a single structure can be saved/loaded exported from other game engines etc, global variables cannot
@dbn5847
@dbn5847 4 ай бұрын
Very, very nice. Thank you very much for that video. Was first class entertainment for me 👏👏👏 I liked the part most, where you simplified the "Rule" classes that were actually functions. But isn't there possibly one more elephant in the room? I mean, if a cell can be dead or alive, and all living cells are unique, defined by their grid position: can't we perhaps represent the "grid" by a set of (x, y) pairs? If a pair is in the set, the cell is alive, if not, it's dead. Which is eventually the most common state for most cells, most of the time. B.t.w., I guess, counting the living cells then turns into a simple len(grid) call, no list comprehension needed.
@ArjanCodes
@ArjanCodes 4 ай бұрын
Indeed, I didn’t spend too much time on improving the actual algorithm itself, I mostly wanted to focus on the design.
@schizobomb
@schizobomb 4 ай бұрын
Maybe the imports could be fixed by installing the project with poetry using the --no-root option ?
@xiggywiggs
@xiggywiggs 4 ай бұрын
yessssss code roast!!
@gz6616
@gz6616 4 ай бұрын
I got one question regarding the return type of __init__(): does it return None, or nothing at all?
@plato4ek
@plato4ek 4 ай бұрын
these are the same
@seppeljordan
@seppeljordan 4 ай бұрын
@@plato4ek (In python)
@wisanuupatumphun7831
@wisanuupatumphun7831 4 ай бұрын
Thank you
@ArjanCodes
@ArjanCodes 4 ай бұрын
Glad it was helpful!
@somethingness
@somethingness 4 ай бұрын
"LIfe is a zero-player game." ... ... ... i think i just achieved enlightenment.
@difegam3
@difegam3 4 ай бұрын
It may sound very geeky, but how satisfying and knowledge-enriching it is to watch these kinds of videos! Thanks Arjan! 👏🏽 You should consider starting a code roast channel 😄 Like for more Code Roast
@ArjanCodes
@ArjanCodes 4 ай бұрын
Glad you like the roasts 😊
@user-pc4sb1dg5q
@user-pc4sb1dg5q 4 ай бұрын
Can yo make a video on pytest?
@VictorGoba
@VictorGoba 4 ай бұрын
Please, do a pytest video for test-noobs. Thanks!
@ajinkyakamat7053
@ajinkyakamat7053 4 ай бұрын
I would have used a dict instead of using a list of lists as the grid representation. That way your game can be infinete by infinite, like the real Convay's Game
@ArgumentumAdHominem
@ArgumentumAdHominem 4 ай бұрын
Next step - performance roast
@JusticeNDOU
@JusticeNDOU 4 ай бұрын
favourite show Code Roast
@nicktids
@nicktids 4 ай бұрын
Thanks arjan the law of Demeter never heard off before off to google. But also I was screaming to use ctrl d or cmd d on Mac to highlight and change all the same variables in one go
@CharleswoodSpudzyofficial
@CharleswoodSpudzyofficial 4 ай бұрын
YEEEEEEEEAAAAAAH!
@Scarhandtunes
@Scarhandtunes 4 ай бұрын
CODE ROAST!!!
@andrewmenshicov2696
@andrewmenshicov2696 4 ай бұрын
i don't agree with using [...].count(True) at 21:50 it means creating a list and only then counting the values. why not just count them on the fly using a generator expression? sum(func(...) for ... in ...) does exactly what is needed, without creating a list 😇
@punstress
@punstress 4 ай бұрын
Awesome! But I never really got a sense of what the game does.
@kennyostrom3098
@kennyostrom3098 4 ай бұрын
He skipped that because it's pretty old and well known. Of course, everyone finds it for the first time at least once -- search for "Conway game of life"
@xravenx24fe
@xravenx24fe 4 ай бұрын
I feel like for Life if youre not trying to optimize away from a simple array model, theres really no reason to overcomplicate or abstract the logic when its literally only present in one execution point of your code. The logic will always be coupled tightly with how the data is stored, to uncouple them would require 3x the code to implement the whole thing (even in Python), so it ends up being a mediocre example of good program structure as its a minimal exception to the rules. I could learn a lot from the testing strategies employed, but once again, the logic is so dead simple I wouldnt even write tests as a human only needs to verify it works once before its set in stone and never needs to be touched again
@zoom0211
@zoom0211 4 ай бұрын
and this is the practical approach people should follow!
@repenning1
@repenning1 4 ай бұрын
For god's sake... what kind of madness is this? The game of life consists of 3 simple rules translating trivially into 3 if/then/else if kind of statements applied to an array iterator. Why would you want to blow up these rules into functions (which only get called once) let alone turning them into classes with static methods!! And, why would you want to use multiple files, modules, import statements, unit tests, etc. at all? If you need significantly more than 40 lines of code for the whole thing something is going terribly wrong. The beauty of programming would be using Conway' and adding as little overhead as possible. Here we are witnessing the opposite. Was Lupos's goal to blow up trivial code with SE principles into some max bloat code? Little detail, is_cell_in_bounds should not be used. Generally speaking, the grid in the game of life is toroidal (wrap around). Just use [(row + 1) % size] types references to implement.
@zoom0211
@zoom0211 4 ай бұрын
Clean Code == Communism xD, in theory it works, in practice it is what it is - quite fckd up
@IsmeGenius
@IsmeGenius 4 ай бұрын
The game of life actually has many potential sets of rules, those 3 are just minimum set to get interesting effects. Anyway, this is exercise, not an attempt to make a product. It is ok to do complex exercises with simple code, it is the whole point of exercise. And we are not talking about overhead in python). It is pointless.
@repenning1
@repenning1 4 ай бұрын
@@IsmeGenius This is not about overhead in Python. By overhead I am not referring to runtime overhead but to cognitive overhead. It's about inflating a simple and highly elegant idea into some software engineering madness to the point where the original idea is no longer recognizable. This is what is wrong with so much of contemporary software development. SE principles are applied in such large doses that good programming practice gets lost. Also, the Game of Life really only has 3 rules. You are thinking of the more general idea of cellular automata which, indeed, can have much more than 3 rules.
@seppeljordan
@seppeljordan 4 ай бұрын
I would kindly disagree with the notion that pytest functions are easier to read then unittest classes. At a first glance it appears so but everyone who dealt with multiple layers of fixtures, some of them autouse, some of them not autouse, different life times of fixtures and so on can tell you that the readability of pytest strongly depends on the discipline of the author/programmer. I think that unittest is easier to get right (especially for newcomers to testing) as it encourages the programmer to be explicit about the setup code of tests.
4 ай бұрын
Captions are out of sync with the audio. hard to follow.
@ArjanCodes
@ArjanCodes 4 ай бұрын
Hey Bruno! Sorry for the inconvenience the problem has now been fixed! :)
@3wcdev878
@3wcdev878 4 ай бұрын
Leonard Hofstadter it's you?
@evandrofilipe1526
@evandrofilipe1526 4 ай бұрын
My favourite part is when the code gets roasted 🎉 edit: 5:53 That code returns a boolean I think because the expression reads: (self.grid[row][col]) == (1 if self.cell_is_cell_in_bounds(row, col) else 0)
@Alek-dr7xn
@Alek-dr7xn 4 ай бұрын
there are no parentheses so Arjan is right
@evandrofilipe1526
@evandrofilipe1526 4 ай бұрын
@@Alek-dr7xn I know there is no parens but I assume it works without them right? I actually don't know, too lazy to open a python shell right now
@aflous
@aflous 4 ай бұрын
No. You need to review operators precedence I guess 🤕
@klaushenger9559
@klaushenger9559 4 ай бұрын
22:17 I'd like to disagree here. There's no need to create a list only to sum up the number of True values inside the list. I'd prefer something along the lines of : return sum(self.is_alive(row + off_y, col + off_x) for off_y, off_x in offsets) It's more memory efficient and probably faster.
@Dyanosis
@Dyanosis 4 ай бұрын
I mean, you're still having to iterate over a list/do list comprehension. Though I do agree that running things by offsets does save memory and should also save time since you're not having to iterate over the entirety, just a portion.
@klaushenger9559
@klaushenger9559 4 ай бұрын
​@@Dyanosis Yeah, we have to iterate over the offset. But my point was that we don't need to create a list or do a list comprehension considerng that we only want to know how many cells are alive. My proposed approach uses a generator, not a list comprehension which means that no memory is used contrary to a list comprehension. Broadly speaking, it's just syntactic sugar for sum_value = 0 for off_y, off_x in offsets: sum_value += int(self.is_alive(row + off_y, col + off_x)) return sum_value That being said, I'm aware that performance or memory efficiency wasn't the main focus of the video but I guess it's worth mentioning, though :).
@kennyostrom3098
@kennyostrom3098 4 ай бұрын
His decision was driven by his choice to make is_alive return bool, even though he's happy leaving the state as an int. Normally, I would prefer something that takes an iterator because constructing a list just to do a count is a bad idea, but here the length is fixed at a single digit size, so it's not a concern, and it's very readable.
@broomva
@broomva 4 ай бұрын
There is a simpler implementation. I can't recall where I got it from so apologies to the original developer, and of course it's not following any of the best practices described in the video haha, so you can roast it too 😅 import time import numpy as np import pygame COLOR_BG = (10, 10, 10) COLOR_GRID = (40, 40, 40) COLOR_DIE_NEXT = (170, 170, 170) COLOR_ALIVE_NEXT = (255, 255, 255, 255) RES_X = 1060 RES_X_P = int((RES_X / 10)) print(RES_X_P) RES_Y = 840 RES_Y_P = int((RES_Y / 10)) def update(screen, cells, size, with_progress=False): updated_cells = np.zeros((cells.shape[0], cells.shape[1])) for row, col in np.ndindex(cells.shape): alive = np.sum(cells[row - 1 : row + 2, col - 1 : col + 2]) - cells[row, col] color = COLOR_BG if cells[row, col] == 0 else COLOR_ALIVE_NEXT if cells[row, col] == 1: if alive < 2 or alive > 3: if with_progress: color = COLOR_DIE_NEXT elif 2
@PrivateKero
@PrivateKero 4 ай бұрын
I have mixed feelings about the changes. I think your explanations are very superficial and not entirely comprehensible. You have defragmented the code a lot. For such a small project, you could argue that this is complete overkill. It might be nice if the developer of the code actually had specific wishes as to what should be paid attention to (as with TheCherno's code reviews). As it is, it actually looks like "this is how Arjan would have done it". And not what you can actually learn from it. Basically, you address big topics like type hinting or unit-testing etc. but as a viewer, I don't really gain an understanding of them here, you just rush from A to B. I can understand that you don't want to repeat yourself too much. I think you already have some videos on type hinting, but maybe it would have been better not to go into it in this code review and leave the code as it is, with the note that you would have liked type hinting here. The complete change to type hinting (with all the other things) seems too massive and for me as a viewer, I don't really recognize the benefit because you basically changed everything.
@Geza_Molnar_
@Geza_Molnar_ 4 ай бұрын
Hello, I am very happy that type hints are emphasised on a channel that has almost a quarter of a million subscribers. There is a short explanation in the video. It's unfortunate that it needs to be mentioned during refactoring, IMHO.
@PrivateKero
@PrivateKero 4 ай бұрын
@@Geza_Molnar_ I hope I have not expressed myself incorrectly. I'm totally with you type hinting in Python is incredibly great and important. But I wouldn't have gained this understanding from this code review. I would need another source. And instead of addressing many important topics, but only treating them superficially, I think it would be better to select a few of them and talk about them in more detail in the video. At least that's how I'd prefer it.
@Geza_Molnar_
@Geza_Molnar_ 4 ай бұрын
@@PrivateKero I'm with you about typing. And I'm happy that important topics are mentioned at any point they are relevant, even superficialy. Creating software / system is a craft that requires to consider many aspects, possibly a few dozens - like security, GDPR, readability, maintainability, performance, user experience, budget, concurrency (the chaos of topics is intentional :-) ). All of them considered, and all of them considered the same time. That's my view on the craft / engineering. (aka "That's the way." :-) ). Not one by one - because here we don't talk about amateurs who are working on a hobby pet project, but professional software / system development. Do we? I see at many places, also experienced at work, that clever and experienced people lean towards the one-by-one approach. An example from Life: first we deal with code and system documentation (an optimist's forecast was 1 year), and than we'll decide if we'll take GDPR or performance, and when those are finished then we'll choose... We had arrived to this point because the same approach was followed previously, too. My opinion is, that it should be emphasised in all tutorials what makes software development a craft AND that the current tutorial addresses whatever specific 1 or 2 aspects of those dozens. Many times, I watch a video and at the end I have the hallucination(!) that when I do those 3 or 5 or 7 things mentioned there then I already know the full craft. Very dangerous because it takes away the opportunity to get to know what I don't know!!! That's how it induces the hallucination. Have a nice day! 🙂
@PrivateKero
@PrivateKero 4 ай бұрын
@@Geza_Molnar_ Hmm, I would disagree a little. I think if you've never heard of all the things Arjan mentions in the video, you're probably a beginner. In my view, this is therefore aimed more at hobby developers or young professionals. For a beginner, it can be completely overwhelming to grasp all these different concepts. The code is just completely different. I think that can be daunting. If you're an experienced programmer, it can certainly be satisfying to watch, but I don't think it has any real added value for you (or only a little). I can understand your point that it's important to address all these topics, but for the pure learning effect, I don't see any benefit.
@CottidaeSEA
@CottidaeSEA 4 ай бұрын
Extremely overengineered for the purpose of Game of Life, but it shows some really neat things one can do in a real project.
@hamzafarhan5429
@hamzafarhan5429 4 ай бұрын
13:34 Hey Arjan, this would fix it: from ..rules import LonleyDeathRule And for the files at the level of rules: from .rules import LonleyDeathRule
@Gamesaucer
@Gamesaucer 4 ай бұрын
Excellent video, though I do have a small nitpick: the way you refactor the tests is bad practice. You can write a test that succeeds for even the most awful code, but you can't write awful code that passes a good test. So you should define the intended program behaviour first by writing the test, and then implement it after. The first time you run your tests after changing their definition, they should be failing. You can also think of tests as your to-do list. It makes little sense to write a to-do list when you're already done.
@Argletrough
@Argletrough 4 ай бұрын
Honestly that starting code just makes me sad. This person is presumably a novice, but they've already been brainwashed to think they need stuff like those classes for the game rules. If OO ever makes sense, it's only for projects many orders of magnitude more complex than this.
@iliya-malecki
@iliya-malecki 4 ай бұрын
It is painful to watch a game that can be implemented in literally 3 lines of code get turned into this monstrosity. And yes i know this is just an example but this is also an example of unfathomable code bloat
@zoom0211
@zoom0211 4 ай бұрын
Wanna roast by an experienced programmer? Both solutions are too freaking big! The 2nd solution is even worse, so many tiny files... Is this yet another SuperTinyFileFactory? Don't make this big mistake in your systems! I followed this stupid rule and ended up with 311 test files (114 LOC on AVG) and 540 src files (103 LOC on AVG) in the system I work on for a few years full-time already. It's a shitload of files! Be fcking careful with those in-objective nonsense rules they're trying to teach you. Try to simplify by making your code shorter. Try to make it faster. Try to write the simplest solution you can. Don't bother with those stupid rules that make you try to predict the future, i.e. what kind of changes you will need to make! You don't know! And everything will go to the trashcan when your client comes and tells you about the new freaking idea for a feature he has. I wasted so much time due to the brainwashing like this! Don't buy these in-measurable rules, it's a nonsense in the long run. Keep the distance folks! Don't bloat your codebases!
@Dyanosis
@Dyanosis 4 ай бұрын
But you didn't solve the stupid naming convention where Grid has a grid. Because that's so much fun to read in code when devs do it. It's like saying that Person has a person object. What does that even mean? Why isn't "grid" just called "cells", since a Grid has cells? Disappointing code roast. Was super excited to see a new one and now I'm just disappointed.
@sauravsingh9177
@sauravsingh9177 4 ай бұрын
Arjan Valley ( the Indian version of Arjan's code roast
@popplestones886
@popplestones886 4 ай бұрын
Shouldnt your tests use game.raw_grid instead of game.grid.grid
Refactoring A Tower Defense Game In Python // CODE ROAST
36:49
ArjanCodes
Рет қаралды 256 М.
What the Heck Are Monads?!
21:08
ArjanCodes
Рет қаралды 68 М.
СНЕЖКИ ЛЕТОМ?? #shorts
00:30
Паша Осадчий
Рет қаралды 6 МЛН
IS THIS REAL FOOD OR NOT?🤔 PIKACHU AND SONIC CONFUSE THE CAT! 😺🍫
00:41
Just try to use a cool gadget 😍
00:33
123 GO! SHORTS
Рет қаралды 43 МЛН
Refactoring A PDF And Web Scraper Part 1 // CODE ROAST
37:44
ArjanCodes
Рет қаралды 43 М.
The Ultimate Guide to Writing Functions
24:31
ArjanCodes
Рет қаралды 177 М.
Life, Death and the Monster (John Conway) - Numberphile
9:04
Numberphile
Рет қаралды 595 М.
epic conway's game of life
6:33
Rational Animations
Рет қаралды 5 МЛН
Protocol Or ABC In Python - When to Use Which One?
23:45
ArjanCodes
Рет қаралды 196 М.
Reacting to Controversial Opinions of Software Engineers
9:18
Fireship
Рет қаралды 2 МЛН
The Incredible Story of The Game of Life
9:33
The Dev Doctor
Рет қаралды 34 М.
Python 3.12 Generic Types Explained
18:27
ArjanCodes
Рет қаралды 57 М.
Why Use Design Patterns When Python Has Functions?
23:23
ArjanCodes
Рет қаралды 98 М.
СНЕЖКИ ЛЕТОМ?? #shorts
00:30
Паша Осадчий
Рет қаралды 6 МЛН