Cool! One suggestion I have is to use DispGraphClrDraw. It would make the program faster, but I'm not sure about smaller.
True. Plus, since it's a tutorial, optimizing the hell out of it might not be the best idea, if only for readability's sake.
I think it would make it bigger, as long as you already have the ClrDraw and DispGraph commands elsewhere.Cool! One suggestion I have is to use DispGraphClrDraw. It would make the program faster, but I'm not sure about smaller.Smaller, too.
Looks great! Are you going to keep the ship on the screen?
I think it would make it bigger, as long as you already have the ClrDraw and DispGraph commands elsewhere.Cool! One suggestion I have is to use DispGraphClrDraw. It would make the program faster, but I'm not sure about smaller.Smaller, too.
Code: (ASHMUP) [Select] :.SHMUP An awesome game. | Code: (ASHMUP Optimized) [Select] :.SHMUP An awesome game. |
Code: (ASHMUPK) [Select] :..KEYS | Code: (ASHMUPK Optimized) [Select] :..KEYS |
Code: (ASHMUPR) [Select] :..ROUTINES | Code: (ASHMUPR Optimized) [Select] :..ROUTINES |
Code: (ASHMUP) [Select]:.SHMUP An awesome game.
:prgmASHMUPD
:prgmASHMUPI
:Repeat getKey(15)
:C+1→C
:prgmASHMUPK
:prgmASHMUPM
:ClrDraw
:If B
:For(I,1,B)
:Pt-On({I*4+L₄-2→J},{J+1},Pic0)
:End
:End
:Pt-Change(X,Y,GDB0)
:DispGraph
:End
:prgmASHMUPR Code: (ASHMUP Optimized) [Select]:.SHMUP An awesome game.
:prgmASHMUPD
:prgmASHMUPI
:Repeat 0
:C+1→C
:prgmASHMUPK
:ClrDraw
:B
:While
:-1→I
:If {*4+L₄+2→J+1}+{J-1}→K*4-256<996
:Pt-On({J},K,→{J+1}Pic0)
:Else
:Isub(RSB)
:End
:I
:End
:Pt-On(X,Y,GDB0)
:DispGraph
:EndIf getKey(15)
:Return
:prgmASHMUPR
Optimization notes (savings listed in brackets):
- [3 bytes, 10 cycles per iteration] – Main getKey(15) loop changed to post-check format
- [31 bytes, ~104 cycles per bullet] – prgmASHMUPM merged into the already existing bullet drawing loop, removing the need for 2 separate loop structures
- [8 bytes] – Removed check that B≠0 before the bullet loop; it is unnecessary because the loop already handles this properly
- [8 bytes, ~38 cycles per bullet] – Bullet loop structure optimized
- [3 bytes, 16 cycles] – Due to the bullet loop no longer being a For() loop, the value of the loop variable can now be carried into the first function inside the loop
- [3 bytes, 12 cycles] – Bullet velocity added to y value instead of subtracted from y value
- [⁻3 bytes, ⁻26 cycles] – Bullet offscreen check changed from <127 to *4-256<996; this is a more compact emulation of (VALUE<64) or (VALUE>248) which would appear to me to be an improved logic statement for this scenario
- [9 bytes, 57 cycles] – J value when checking bullet y value reused to get x value for Pt-On() simply with {J}
- [6 bytes, 43 cycles] – Reused the value stored in J from the offscreen check to get the x coordinate for Pt-On()
- [4 bytes, ~18 cycles] – Stored the bullet y value into a variable when checking the y value to avoid needing to retrieve again later in the Pt-On() command; also moved the code to store the new y value to the bullet's data in the middle of the Pt-On() command so the fact that it returns a pointer doesn't disrupt calculations
- [3 bytes, 16 cycles] – sub(RSB) called without saving the argument into r1 first
- [126 bytes] – Player sprite drawn with Pt-On() instead of Pt-Change(), removing the need for a second bulky sprite routine
- [-1 bytes, ? cycles] – A Return added after the main loop, before prgmASHMUPR; without it, after the user presses clear, program execution would just continue into the first subroutine in prgmASHMUPR, potentially causing big problems
Code: (ASHMUPK) [Select]:..KEYS
:If getKey(1)
:Y+2→Y
:End
:If getKey(2)
:X-2→X
:End
:If getKey(3)
:X+2→X
:End
:If getKey(4)
:Y-2→Y
:End
:If getKey(54)
:!If C^8
:sub(ASB,1,3,X+3,Y-4)
:End
:End Code: (ASHMUPK Optimized) [Select]:..KEYS
:!If ^8
:If getKey(54)
:sub(ASB,,⁻3,X+3,Y-4)
:End
:End
:!If getKey(3)-getKey(2)+1*2+X
:+2
:End
:min(-2,88)→X
:!If getKey(1)-getKey(4)+1*2+Y
:+2
:End
:min(-2,56)→Y
Optimization notes:
- [3 bytes, 16 cycles] – Weapon fire checking moved to the top and the counter check moved before the key check, allowing for the counter value to be carried from the previous statement in prgmASHMUP
- [3 bytes, 10 cycles] – Omitted the bullet type from sub(ASB), as a 1 can be carried from the getKey(54) check
- Bullet velocity changed from 3 to ⁻3, as the velocity is now added to the y value instead of subtracted from it
- [⁻22 bytes, too lazy to calculate cycles] – New approach to movement key checking; also makes sure the ship doesn't leave the screen
Code: (ASHMUPR) [Select]:..ROUTINES
:Lbl ASB
:If B<80
:r₄→{r₃→{r₂→{r₁→{B+1→B*4+L₄-4}+1}+1}+1}
:End
:Return
:Lbl RSB
:Copy(r₁*4+L₄+4,-4,B-1→B-r₁*4+1)
:Return Code: (ASHMUPR Optimized) [Select]:..ROUTINES
:Lbl ASB
:Return!If B-80
:r₄→{r₃→{r₂→{r₁→{B+1→B*4+L₄-4}+1}+1}+1}
:Return
:Lbl RSB
:Copy(→r₁*4+L₄+4,-4,B-1→B-r₁*4+1)
Optimization notes:
- [2 bytes, ~5 cycles] – Changed the check that the number of bullets is not too high from an if statement into a conditional return statement
- [5 bytes, 20 cycles] – Changed If B<80 to !If B-80; the second is an optimized version of If B=80, which should work equally well because B can be 80 but should never be greater than 80
- [1 byte] – Removed the last Return, as Axe automatically adds a Return at the end of the program
Why is post-check more optimized than a Repeat loop, though?
Speaking of optimizing control structures, any chance for Do...While loops?
I'd like Switch cases more than Do...While loops, but I don't really know how the second works.
Do...While loops are a lot like While...End loops, except that the check to advance to another iteration of the loop is at the end instead of the beginning. This turns out to be more optimized. Whereas a While...End loop has two jumps, one to exit the loop if the condition is false and one to jump back to the beginning, a Do...While combines the two into one jump that jumps back to the beginning if the condition is true.
I don't know how good that explanation was, but perhaps this pseudocode will help clarify it. Numbers in brackets indicate the number of bytes the real code takes:
While...End loop Do...While loop Code: [Select];While
[x] Loop condition
[2] Check if loop condition is true or false
[3] Exit the loop if false
;Loop contents
;End
[3] Jump back to the beginning of the loop Code: [Select];Do
;[0] Start of the loop, takes no actual code
;Loop contents
;While
[x] Loop condition
[2] Check if loop condition is true or false
[3] Jump back to the beginning of the loop if true
I like leaving the last Return in because it helps code readability, and it makes it easier to add new subroutines after it. It doesn't actually affect the compiled code, right?
Are you going to make it so the player's sprite cannot go off the screen?Looks great! Are you going to keep the ship on the screen?
What do you mean?
Why is post-check more optimized than a Repeat loop, though?
I think I remember mentioning why this is in a previous post somewhere... ah yes, here it is:
I like leaving the last Return in because it helps code readability, and it makes it easier to add new subroutines after it. It doesn't actually affect the compiled code, right?
I agree that it improves readability, but because Axe automatically adds a return to the end of programs it's redundant and a waste of 1 byte. You can always improve readability while maintaining optimization by keeping the final Return, but commenting it out.
Are you going to make it so the player's sprite cannot go off the screen?Looks great! Are you going to keep the ship on the screen?
What do you mean?
Axe actually checks if there's already a Return at the end. If there is, it isn't added.
I swear Quigibo mentioned he optimized the Return...
Maybe it was just a feature request. I'll check.
- Return optimization in no longer automatic due to possible program leaks.