From telackey at lacklustremedia.com Tue Apr 4 19:57:24 2017 From: telackey at lacklustremedia.com (Thomas E. Lackey) Date: Wed, 5 Apr 2017 00:57:24 +0000 Subject: [netrek-dev] Robots, get_course, and casting floats to unsigned types. Message-ID: <7B7F0680CB055E4183BC55AAFC44B75D01BDC5D4F4@iola.redbudcomputer.local> Hello! After setting up my new server, I noticed several of my robots camped off in a corner of the galaxy instead of fighting each other and I went on crawl through the robot code to figure out why. According to their 'dstate' debug output, they usually--not always, but that is a different story--thought they were doing something--engaging an enemy, protecting a planet, etc--while in fact they were turning in circles in the upper-right most corner of KLI space. While not a complete answer to robot strangeness, I think one of the culprits is something wrong with the get_course()/get_acourse() functions in robotd/util.c get_course does this to calculate the course: (unsigned char)(atan2((double) (x - me->p_x), (double) (me->p_y - y)) / 3.141592 * 128.); I am fairly certain per-C99 that the result of casting a negative floating point to any unsigned type is undefined, so exactly what happens may vary by architecture and compiler. On my ARM RaspberryPI compiling with gcc, the result is always a value of 0. I separated the value of the calculation before and after the cast and added some logging to confirm: Engaging: Galla get_intercept_course : x: 6735, y: 59554 get_course (UNCORRECTED): ret = -102.354382 , ret_crs = 0 I am not certain how the course system works, but guessing from the fact that it needs to fit in an unsigned char, I am thinking that 127 + the absolute value would be a good guess like this updated log output: get_course (CORRECTED): ret = -102.354382 , ret_crs = 229 After making that change, my robots still do plenty of other strange things now and again, but at least they don't seem to be stuck in the corner of the galaxy spinning in circles. I have two questions: (1) Does all that sound correct? (2) How is the unsigned char-based course system expected to work? 360 degrees carved into 255 slices? Or Something else? My changes are not in a state to check in, but if this sounds reasonable, I will polish them up. Cheers, Thomas Lackey -------------- next part -------------- An HTML attachment was scrubbed... URL: From telackey at lacklustremedia.com Tue Apr 4 20:28:58 2017 From: telackey at lacklustremedia.com (Thomas E. Lackey) Date: Wed, 5 Apr 2017 01:28:58 +0000 Subject: [netrek-dev] Robots, get_course, and casting floats to unsigned types. In-Reply-To: <7B7F0680CB055E4183BC55AAFC44B75D01BDC5D4F4@iola.redbudcomputer.local> References: <7B7F0680CB055E4183BC55AAFC44B75D01BDC5D4F4@iola.redbudcomputer.local> Message-ID: <7B7F0680CB055E4183BC55AAFC44B75D01BDC5D61D@iola.redbudcomputer.local> Looking at the calculation itself (I only glanced at it before) it is obvious the course works on the angle in radians, but even so, any fuller answer on (2) would be appreciated. From: netrek-dev-bounces at us.netrek.org [mailto:netrek-dev-bounces at us.netrek.org] On Behalf Of Thomas E. Lackey Sent: Tuesday, April 04, 2017 7:57 PM To: netrek-dev at us.netrek.org Subject: [netrek-dev] Robots, get_course, and casting floats to unsigned types. Hello! After setting up my new server, I noticed several of my robots camped off in a corner of the galaxy instead of fighting each other and I went on crawl through the robot code to figure out why. According to their 'dstate' debug output, they usually--not always, but that is a different story--thought they were doing something--engaging an enemy, protecting a planet, etc--while in fact they were turning in circles in the upper-right most corner of KLI space. While not a complete answer to robot strangeness, I think one of the culprits is something wrong with the get_course()/get_acourse() functions in robotd/util.c get_course does this to calculate the course: (unsigned char)(atan2((double) (x - me->p_x), (double) (me->p_y - y)) / 3.141592 * 128.); I am fairly certain per-C99 that the result of casting a negative floating point to any unsigned type is undefined, so exactly what happens may vary by architecture and compiler. On my ARM RaspberryPI compiling with gcc, the result is always a value of 0. I separated the value of the calculation before and after the cast and added some logging to confirm: Engaging: Galla get_intercept_course : x: 6735, y: 59554 get_course (UNCORRECTED): ret = -102.354382 , ret_crs = 0 I am not certain how the course system works, but guessing from the fact that it needs to fit in an unsigned char, I am thinking that 127 + the absolute value would be a good guess like this updated log output: get_course (CORRECTED): ret = -102.354382 , ret_crs = 229 After making that change, my robots still do plenty of other strange things now and again, but at least they don't seem to be stuck in the corner of the galaxy spinning in circles. I have two questions: (1) Does all that sound correct? (2) How is the unsigned char-based course system expected to work? 360 degrees carved into 255 slices? Or Something else? My changes are not in a state to check in, but if this sounds reasonable, I will polish them up. Cheers, Thomas Lackey -------------- next part -------------- An HTML attachment was scrubbed... URL: From quozl at us.netrek.org Tue Apr 4 21:10:31 2017 From: quozl at us.netrek.org (James Cameron) Date: Wed, 5 Apr 2017 12:10:31 +1000 Subject: [netrek-dev] Robots, get_course, and casting floats to unsigned types. In-Reply-To: <7B7F0680CB055E4183BC55AAFC44B75D01BDC5D4F4@iola.redbudcomputer.local> References: <7B7F0680CB055E4183BC55AAFC44B75D01BDC5D4F4@iola.redbudcomputer.local> Message-ID: <20170405021031.GC2639@us.netrek.org> Yeah, I've seen that before with ARM, several years ago. The network protocol uses an unsigned char to represent a direction. Netrek was written for 32-bit systems before i386 was a thing, and was later ported to i386 and tested. Testing concentrated on real players, and rarely robots. You're welcome to do a port to ARM and test. I'll review code changes. Other places of interest can be found with a "git grep atan2", where you will find; (a) some code which drops the result in a signed variable, then where it is negative add 256 to bring it into the unsigned char representation (ntserv/daemon.c, ntserv/util.c, robotd/dodge.c), (b) some other code using the same pattern you identified (robots/puckmove.c, xsg/input.c, xsg/redraw.c), (c) some code using a similar pattern as (a) but where the limiting is done before scaling (ntserv/wander2.c, tools/fun.c), There will also be other interesting casts to investigate. Big job. C99 not particularly relevant, given it came out way after Netrek was big. -- James Cameron http://quozl.netrek.org/ From quozl at us.netrek.org Tue Apr 4 22:23:25 2017 From: quozl at us.netrek.org (James Cameron) Date: Wed, 5 Apr 2017 13:23:25 +1000 Subject: [netrek-dev] Robots, get_course, and casting floats to unsigned types. In-Reply-To: <20170405021031.GC2639@us.netrek.org> References: <7B7F0680CB055E4183BC55AAFC44B75D01BDC5D4F4@iola.redbudcomputer.local> <20170405021031.GC2639@us.netrek.org> Message-ID: <20170405032325.GD2639@us.netrek.org> And for completeness, some of the implementation of protocol representations can be considered as fixed-point arithmetic. Fixed-point is a favourite of mine, and I've used it frequently in embedded systems and Forth. See https://en.wikipedia.org/wiki/Fixed-point_arithmetic -- James Cameron http://quozl.netrek.org/ From mrbax.nospam.2007 at gmail.com Sat Apr 8 00:54:21 2017 From: mrbax.nospam.2007 at gmail.com (Michael Bax) Date: Sat, 8 Apr 2017 07:54:21 +0200 Subject: [netrek-dev] Robots, get_course, and casting floats to unsigned types. Message-ID: Hi Thomas > (unsigned char)(atan2((double) (x - me->p_x), > (double) (me->p_y - y)) / 3.141592 * 128.); > > I am fairly certain per-C99 that the result of casting a negative floating > point to any unsigned type is undefined, so exactly what happens may vary > by architecture and compiler. "If the value of the integral part cannot be represented by the integer type, the behavior is undefined. ? The remaindering operation performed when a value of integer type is converted to unsigned type need not be performed when a value of real floating type is converted to unsigned type." Solution: (unsigned char)(int)(atan2((double) (x - me->p_x), (double) (me->p_y - y)) / 3.141592 * 128.) But the value of pi is incorrectly rounded and those double casts are unnecessary; in any case we clearly do not need double precision: (unsigned char)(int)(atan2f((x - me->p_x), (me->p_y - y)) / 3.141593 * 128.) > I am not certain how the course system works, but guessing from the fact > that it needs to fit in an unsigned char, I am thinking that 127 + the > absolute value would be a good guess > (1) Does all that sound correct? Not quite. Due north 0.0 maps to 0, not 127. > (2) How is the unsigned char-based course system expected to work? > 360 degrees carved into 255 slices? Or Something else? It is 360 degrees unequally divided into 256 slices: "0" has twice the normal width and "180" has zero width. Fixing this requires rounding rather than truncation. With C99 and IMO clearer layout: (unsigned char) (int) ( roundf( 128.f*atan2f( dx, dy )/3.141593f ) ) Cheers Michael From mrbax.nospam.2007 at gmail.com Sat Apr 8 12:13:56 2017 From: mrbax.nospam.2007 at gmail.com (Michael Bax) Date: Sat, 8 Apr 2017 19:13:56 +0200 Subject: [netrek-dev] Robots, get_course, and casting floats to unsigned types. Message-ID: > But the value of pi is incorrectly rounded and those double casts are > unnecessary; in any case we clearly do not need double precision: > (unsigned char)(int)(atan2f((x - me->p_x), (me->p_y - y)) / 3.141593 * 128.) Oops, wrong draft. :-P That should be: (unsigned char)(int)(atan2f(x - me->p_x, me->p_y - y) / 3.141593f * 128.f) > Fixing this requires rounding rather than truncation. With C99 and > IMO clearer layout: > (unsigned char) (int) ( roundf( 128.f*atan2f( dx, dy )/3.141593f ) ) And that should be: (unsigned char) (int) ( roundf( 128.f*atan2f( x - me->p_x, me->p_y - y )/3.141593f ) ) Final note: "0" being due north assumes that the x-axis points east and the y-axis points south. Cheers Michael From quozl at us.netrek.org Thu Apr 13 01:14:29 2017 From: quozl at us.netrek.org (James Cameron) Date: Thu, 13 Apr 2017 16:14:29 +1000 Subject: [netrek-dev] Robots, get_course, and casting floats to unsigned types. In-Reply-To: References: Message-ID: <20170413061429.GE25214@us.netrek.org> How's this? https://github.com/quozl/netrek-server/commit/a6c3680fc8fa2093fbd9de2dbb4f4e44d9f7a95b -- James Cameron http://quozl.netrek.org/ From mrbax.nospam.2007 at gmail.com Thu Apr 13 11:07:53 2017 From: mrbax.nospam.2007 at gmail.com (Michael Bax) Date: Thu, 13 Apr 2017 18:07:53 +0200 Subject: [netrek-dev] Robots, get_course, and casting floats to unsigned types. In-Reply-To: <20170413061429.GE25214@us.netrek.org> References: <20170413061429.GE25214@us.netrek.org> Message-ID: Hi James > How's this? > > https://github.com/quozl/netrek-server/commit/a6c3680fc8fa2093fbd9de2dbb4f4e44d9f7a95b Here is my take: https://github.com/mrbax/netrek-server/commit/226aea74701e01eeada833f91cc8f48ac72f046d I primarily eliminated some unnecessary casts and sign protection, substituted M_PI since it was used elsewhere, and switched to the non-exception-raising nearbyint*(). The rest was mostly related clean-up. Cheers Michael From telackey at lacklustremedia.com Thu Apr 13 13:52:49 2017 From: telackey at lacklustremedia.com (Thomas E. Lackey) Date: Thu, 13 Apr 2017 18:52:49 +0000 Subject: [netrek-dev] Robots, get_course, and casting floats to unsigned types. In-Reply-To: References: <20170413061429.GE25214@us.netrek.org> Message-ID: <7B7F0680CB055E4183BC55AAFC44B75D01BDC7CB6D@iola.redbudcomputer.local> A quick comment, but I just realized every one of these messages had gone to my spam folder. I just figured things were quiet... I need to read from the beginning to get up to speed on developments. Cheers, -- Thomas E Lackey > -----Original Message----- > From: netrek-dev-bounces at us.netrek.org [mailto:netrek-dev- > bounces at us.netrek.org] On Behalf Of Michael Bax > Sent: Thursday, April 13, 2017 11:08 AM > To: netrek-dev at us.netrek.org > Subject: Re: [netrek-dev] Robots, get_course, and casting floats to unsigned > types. > > Hi James > > > How's this? > > > > https://github.com/quozl/netrek- > server/commit/a6c3680fc8fa2093fbd9de2d > > bb4f4e44d9f7a95b > > Here is my take: > https://github.com/mrbax/netrek- > server/commit/226aea74701e01eeada833f91cc8f48ac72f046d > > I primarily eliminated some unnecessary casts and sign protection, > substituted M_PI since it was used elsewhere, and switched to the non- > exception-raising nearbyint*(). The rest was mostly related clean-up. > > Cheers > Michael > _______________________________________________ > netrek-dev mailing list > netrek-dev at us.netrek.org > http://mailman.us.netrek.org/mailman/listinfo/netrek-dev From quozl at us.netrek.org Thu Apr 13 22:01:55 2017 From: quozl at us.netrek.org (James Cameron) Date: Fri, 14 Apr 2017 13:01:55 +1000 Subject: [netrek-dev] Robots, get_course, and casting floats to unsigned types. In-Reply-To: References: <20170413061429.GE25214@us.netrek.org> Message-ID: <20170414030155.GE31309@us.netrek.org> On Thu, Apr 13, 2017 at 06:07:53PM +0200, Michael Bax wrote: > I primarily eliminated some unnecessary casts and sign protection, > substituted M_PI since it was used elsewhere, and switched to the > non-exception-raising nearbyint*(). The rest was mostly related > clean-up. M_PI on my systems is a double literal, so this will promote the other arguments to double, then another conversion to float before passing to nearbyintf, right? The original use of double instead of float was more about the definition of atan2, but now that we have atan2f the situation has changed. -- James Cameron http://quozl.netrek.org/ From mrbax.nospam.2007 at gmail.com Fri Apr 14 12:43:35 2017 From: mrbax.nospam.2007 at gmail.com (Michael Bax) Date: Fri, 14 Apr 2017 19:43:35 +0200 Subject: [netrek-dev] netrek-dev Digest, Vol 90, Issue 6 In-Reply-To: References: Message-ID: Hi James >> substituted M_PI since it was used elsewhere > > M_PI on my systems is a double literal, so this will promote the other > arguments to double, then another conversion to float before passing > to nearbyintf, right? Indeed! Good catch. https://github.com/mrbax/netrek-server/commit/c53069112920806bcb87429a7e86354920a9cd48 Cheers Michael From xyzzy at speakeasy.org Fri Apr 14 14:01:17 2017 From: xyzzy at speakeasy.org (Trent Piepho) Date: Fri, 14 Apr 2017 12:01:17 -0700 Subject: [netrek-dev] netrek-dev Digest, Vol 90, Issue 6 In-Reply-To: References: Message-ID: Those constants aren't accurate to the limit of a float. E.g., (float)M_PI/2.0 == 1.57079637f but (float)M_PI/2.0 != 1.5707964f. Eight digits should be enough. It'd be nicer to combine all the 128.0f and M_PI into one constant. E.e. RAD_TO_BRAD (float)(128.0 * M_PI). Binary radians or binary degrees is what these units get called. Even better, make an inline function to handle all the cases instead of repeating the code. I've got to wonder why some are offset by -90 degrees and some aren't? On Fri, Apr 14, 2017 at 10:43 AM, Michael Bax wrote: > Hi James > >>> substituted M_PI since it was used elsewhere >> >> M_PI on my systems is a double literal, so this will promote the other >> arguments to double, then another conversion to float before passing >> to nearbyintf, right? > > Indeed! Good catch. > > https://github.com/mrbax/netrek-server/commit/c53069112920806bcb87429a7e86354920a9cd48 > > Cheers > Michael > _______________________________________________ > netrek-dev mailing list > netrek-dev at us.netrek.org > http://mailman.us.netrek.org/mailman/listinfo/netrek-dev > From quozl at us.netrek.org Fri Apr 14 16:19:48 2017 From: quozl at us.netrek.org (James Cameron) Date: Sat, 15 Apr 2017 07:19:48 +1000 Subject: [netrek-dev] netrek-dev Digest, Vol 90, Issue 6 In-Reply-To: References: Message-ID: <20170414211948.GA24124@us.netrek.org> On Fri, Apr 14, 2017 at 12:01:17PM -0700, Trent Piepho wrote: > I've got to wonder why some are offset by -90 degrees and some > aren't? Sign of y input. Most code has pattern atan2(x - mx, my - y), but code for torpedo guidance t_track in ntserv/daemon.c uses atan2(x - mx, y - my). Further uses of dy in t_track don't stop us from changing the sign in order to remove the offset. (t_near and t_explosion makes the same calculation of dy but there the sign does not matter.) -- James Cameron http://quozl.netrek.org/ From xyzzy at speakeasy.org Fri Apr 14 19:17:45 2017 From: xyzzy at speakeasy.org (Trent Piepho) Date: Fri, 14 Apr 2017 17:17:45 -0700 Subject: [netrek-dev] netrek-dev Digest, Vol 90, Issue 6 In-Reply-To: <20170414211948.GA24124@us.netrek.org> References: <20170414211948.GA24124@us.netrek.org> Message-ID: On Fri, Apr 14, 2017 at 2:19 PM, James Cameron wrote: > On Fri, Apr 14, 2017 at 12:01:17PM -0700, Trent Piepho wrote: >> I've got to wonder why some are offset by -90 degrees and some >> aren't? > > Sign of y input. My first thought was that's not right. Inverting the y-axis will change the sign of the angle. I.e., atan2(y, x) == -atan2(-y, x) Using normal trigonometry, where the positive x axis is 0 and positive y axis is 90. Angle increase counterclockwise. But... > Most code has pattern atan2(x - mx, my - y), but code for torpedo > guidance t_track in ntserv/daemon.c uses atan2(x - mx, y - my). The argument to atan2 are supposed to by y,x, not x,y, assuming one wants the normal trigonometric convention. But I recall the sin/cos tables in netrek are different, and they're what defines the truth about how to interpret an angle. When one uses atan2(dx, -dy), one has in effect rotated the angle by positive 90 degrees. I.e., atan2(dy, dx) == atan2(dx, -dy) + 90deg // notice x,y order! alternatively: atan2(dy, dx) - 90deg == atan2(dx, -dy) The code in t_track() hasn't just inverted y, it has also swapped the x and y axes. Then it arrives at the same result by adding in a -90 rotation. It'd be faster to do it the way other code does. In a less fractured code base, there should have been some utility to do this so it wouldn't keep getting re-implemented. From mrbax.nospam.2007 at gmail.com Thu Apr 20 09:08:21 2017 From: mrbax.nospam.2007 at gmail.com (Michael Bax) Date: Thu, 20 Apr 2017 16:08:21 +0200 Subject: [netrek-dev] Robots, get_course, and casting floats to unsigned types. Message-ID: > Those constants aren't accurate to the limit of a float. E.g., > (float)M_PI/2.0 == 1.57079637f but (float)M_PI/2.0 != 1.5707964f. printf( "%i\n", (float) ( M_PI/2.0 ) == 1.5707964f ); 1 > The argument to atan2 are supposed to by y,x, not x,y, assuming one > wants the normal trigonometric convention. But I recall the sin/cos > tables in netrek are different, and they're what defines the truth > about how to interpret an angle. Units aside, sin is sin. But the direction of "0" is chosen by the implementation. > In a less fractured code base, there should have been some utility to > do this so it wouldn't keep getting re-implemented. Funny that you should mention that? :-) https://github.com/mrbax/netrek-server/commit/5bcf5964eef8c545c3dda43a16b28ffc60fcd7b0 Cheers Michael From quozl at us.netrek.org Thu Apr 27 05:03:18 2017 From: quozl at us.netrek.org (James Cameron) Date: Thu, 27 Apr 2017 20:03:18 +1000 Subject: [netrek-dev] Robots, get_course, and casting floats to unsigned types. In-Reply-To: References: Message-ID: <20170427100318.GA29887@us.netrek.org> On Thu, Apr 20, 2017 at 04:08:21PM +0200, Michael Bax wrote: > > Those constants aren't accurate to the limit of a float. E.g., > > (float)M_PI/2.0 == 1.57079637f but (float)M_PI/2.0 != 1.5707964f. > > printf( "%i\n", (float) ( M_PI/2.0 ) == 1.5707964f ); > > 1 Yes, so too is (1.57079637f == 1.5707964f), on gcc 4.8.4 x86_64. > > In a less fractured code base, there should have been some utility to > > do this so it wouldn't keep getting re-implemented. > > Funny that you should mention that? :-) > https://github.com/mrbax/netrek-server/commit/5bcf5964eef8c545c3dda43a16b28ffc60fcd7b0 Almost like. As author of to_dir, naming loss is felt, apparently some attachment still ... and the constant 40.743665f is a bit opaque. Otherwise grand. ;-) -- James Cameron http://quozl.netrek.org/ From xyzzy at speakeasy.org Thu Apr 27 08:05:15 2017 From: xyzzy at speakeasy.org (Trent Piepho) Date: Thu, 27 Apr 2017 06:05:15 -0700 Subject: [netrek-dev] Robots, get_course, and casting floats to unsigned types. In-Reply-To: <20170427100318.GA29887@us.netrek.org> References: <20170427100318.GA29887@us.netrek.org> Message-ID: On Thu, Apr 27, 2017 at 3:03 AM, James Cameron wrote: > On Thu, Apr 20, 2017 at 04:08:21PM +0200, Michael Bax wrote: >> > Those constants aren't accurate to the limit of a float. E.g., >> > (float)M_PI/2.0 == 1.57079637f but (float)M_PI/2.0 != 1.5707964f. >> >> printf( "%i\n", (float) ( M_PI/2.0 ) == 1.5707964f ); >> >> 1 > > Yes, so too is (1.57079637f == 1.5707964f), on gcc 4.8.4 x86_64. Indeed it is. It takes 9 decimal digits to represent all floats (24 bit mantissa) with complete accuracy. And 1.5707964f is only eight. But of course 9 digits are the max, any given number might be accurate with fewer. 1f == 1.0000000f after all and that's just one digit. I tested M_PI/2.0 to see how many digits it needed and came up with the max of 9. I must have tested incorrectly somehow because clearly it's 8. Though I still say use 9 digits, because then you know it will be right no matter what number. >> > In a less fractured code base, there should have been some utility to >> > do this so it wouldn't keep getting re-implemented. >> >> Funny that you should mention that? :-) >> https://github.com/mrbax/netrek-server/commit/5bcf5964eef8c545c3dda43a16b28ffc60fcd7b0 > > Almost like. As author of to_dir, naming loss is felt, apparently > some attachment still ... and the constant 40.743665f is a bit opaque. > > Otherwise grand. ;-) I agree. Nicely consistent. Suggest making: #define RAD_TO_BRAD (float)(128f/M_PI)