Binary Search Trees Switching subtrees - c

So I'm trying to write a function that when given two pointers to nodes in the BST, will 'switch' the subtree locations.
typedef struct NODE {
struct NODE* parent;
struct NODE* left;
struct NODE* right;
}node_t;
This is the node struct I have for the BST.
My function goes along the line of :
void switch_subtree(node_t* a, node_t* b)
{
if (a==NULL || b==NULL)
{
return;
}
if (a->parent->left == a)
{
a->parent->left = b;
}
else
{
a->parent->right = b;
}
if (b->parent->left == b)
{
b->parent->left = a;
}
else
{
b->parent->right = a;
}
nodes * temp = a;
a->parent = b->parent;
b->parent = temp->parent;
}
However, when I run it, it does not properly switch the subtrees.
Can anyone point out any errors Im making and point me in the right direction?
Thanks!!!

Your problem is here:
nodes * temp = a;
a->parent = b->parent;
b->parent = temp->parent;
correctly it should read:
nodes * temp = a->parent;
a->parent = b->parent;
b->parent = temp;
otherwise a->parent is forever lost after line 2.
rationale
wrong approach
The line temp = a will make both pointers temp and a point to the same NODE structure:
+- > +--------+ +- > +--------+
| | | | | |
| | ... | | | ... |
| +--------+ | +--------+
| |
+--------------+ +----------------+
| |
+--------+- > +--------+ | +- > +---------+ |
| | | parent |-+ | | parent |-+
| | | ... | | | ... |
| | +--------+ | +---------+
| | |
+------+ | +---+ | +---+ |
| temp |-+ | a |-+ | b |-+
+------+ +---+ +---+
Changing a->parent in line 2 (a->parent = b->parent) will also change temp->parent as both are just different names for the same component (parent) of the same NODE structure:
+--------+ +---+- > +--------+
| | | | | |
| ... | | | | ... |
+--------+ | | +--------+
| |
| +--------------+
| |
+--------+- > +--------+ | +- > +---------+
| | | parent |---+ | | parent |
| | | ... | | | ... |
| | +--------+ | +---------+
| | |
+------+ | +---+ | +---+ |
| temp |-+ | a |-+ | b |-+
+------+ +---+ +---+
The assignment b->parent = temp->parent doesn't change anything at all, as both b->parent and temp->parent are already pointing at the same node.
- mistake !
alternative
Taking a look at the proposed alternative, temp = a->parent will leave you with the situation sketched below:
+---------+- > +--------+ +- > +--------+
| | | | | | |
| | | ... | | | ... |
| | +--------+ | +--------+
| | |
| +--------------+ +----------------+
| | |
| +- > +--------+ | +- > +---------+ |
| | | parent |-+ | | parent |-+
| | | ... | | | ... |
| | +--------+ | +---------+
| | |
+------+ | +---+ | +---+ |
| temp |-+ | a |-+ | b |-+
+------+ +---+ +---+
After a->parent = b->parent temp is still pointing to the original parent node of the node pointed to by a:
+----------- > +--------+ +- > +--------+
| | | | | |
| | ... | | | ... |
| +--------+ | +--------+
| |
| +-----+----------------+
| | |
| +- > +--------+ | +- > +---------+ |
| | | parent |-+ | | parent |-+
| | | ... | | | ... |
| | +--------+ | +---------+
| | |
+------+ | +---+ | +---+ |
| temp |-+ | a |-+ | b |-+
+------+ +---+ +---+
Finally assigning b->parent = temp will give the node pointed to by b the right parent:
+--------+-- > +--------+ +----- > +--------+
| | | | | | |
| | | ... | | | ... |
| | +--------+ | +--------+
| | |
| +-----------------|--------------------+
| | |
| +- > +--------+ | +- > +---------+ |
| | | parent |---+ | | parent |-+
| | | ... | | | ... |
| | +--------+ | +---------+
| | |
+------+ | +---+ | +---+ |
| temp |-+ | a |-+ | b |-+
+------+ +---+ +---+

Related

Understanding Linker output (Elf2Bin)

I'm doing some development using Mbed Studio and after linking I see this sort of output:
Elf2Bin: test-sp2
| Module | .text | .data | .bss |
|----------------------|---------------|---------|------------|
| [lib]\c_w.l | 5900(+196) | 16(+0) | 348(+0) |
| [lib]\fz_wv.l | 26(+0) | 0(+0) | 0(+0) |
| [lib]\libcpp_w.l | 1(+0) | 0(+0) | 0(+0) |
| [lib]\libcppabi_w.l | 44(+0) | 0(+0) | 0(+0) |
| anon$$obj.o | 94(+94) | 0(+0) | 2048(+0) |
| main.o | 1798(+0) | 0(+0) | 460(+0) |
| mbed-os\cmsis | 13531(+0) | 168(+0) | 6609(+0) |
| mbed-os\connectivity | 70226(+140) | 295(+0) | 45532(+40) |
| mbed-os\drivers | 3852(-16) | 0(+0) | 0(+0) |
| mbed-os\events | 2016(+0) | 0(+0) | 3104(+0) |
| mbed-os\hal | 2090(+0) | 8(+0) | 115(+0) |
| mbed-os\platform | 9442(+0) | 64(+0) | 1104(+0) |
| mbed-os\rtos | 1792(+0) | 0(+0) | 8(+0) |
| mbed-os\targets | 34347(+1459) | 296(+0) | 394(+0) |
| Subtotals | 145159(+1873) | 847(+0) | 59722(+40) |
Total Static RAM memory (data + bss): 60569(+40) bytes
Total Flash memory (text + data): 146006(+1873) bytes
I understand what this output means, mostly.
But what do the (+xxx) next to the byte counts mean?
For example, in
| mbed-os\connectivity | 70226(+140) | 295(+0) | 45532(+40) |
What does the (+140) in the .text section mean? Could it be the change in size from the last link?
Could it be the change in size from the last link?
Yes.

Why using char type as index for looping gives unexpected results?

Bear in mind this is an old version of the C compiler: CP/M for Z80.
#include<stdio.h>
main()
{
char i = 0;
do
{
printf("0x%04x | ", i);
} while (++ i);
}
Expected:
0x0000 | 0x0001 | 0x0002 | 0x0003 | 0x0004 | 0x0005 | 0x0006 | 0x0007 | 0x0008 | 0x0009 | 0x000A | 0x000B | 0x000C | 0x000D | 0x000E | 0x000F | 0x0010 | 0x0011 | 0x0012 | 0x0013 | 0x0014 | 0x0015 | 0x0016 | 0x0017 | 0x0018 | 0x0019 | 0x001A | 0x001B | 0x001C | 0x001D | 0x001E | 0x001F | 0x0020 | 0x0021 | 0x0022 | 0x0023 | 0x0024 | 0x0025 | 0x0026 | 0x0027 | 0x0028 | 0x0029 | 0x002A | 0x002B | 0x002C | 0x002D | 0x002E | 0x002F | 0x0030 | 0x0031 | 0x0032 | 0x0033 | 0x0034 | 0x0035 | 0x0036 | 0x0037 | 0x0038 | 0x0039 | 0x003A | 0x003B | 0x003C | 0x003D | 0x003E | 0x003F | 0x0040 | 0x0041 | 0x0042 | 0x0043 | 0x0044 | 0x0045 | 0x0046 | 0x0047 | 0x0048 | 0x0049 | 0x004A | 0x004B | 0x004C | 0x004D | 0x004E | 0x004F | 0x0050 | 0x0051 | 0x0052 | 0x0053 | 0x0054 | 0x0055 | 0x0056 | 0x0057 | 0x0058 | 0x0059 | 0x005A | 0x005B | 0x005C | 0x005D | 0x005E | 0x005F | 0x0060 | 0x0061 | 0x0062 | 0x0063 | 0x0064 | 0x0065 | 0x0066 | 0x0067 | 0x0068 | 0x0069 | 0x006A | 0x006B | 0x006C | 0x006D | 0x006E | 0x006F | 0x0070 | 0x0071 | 0x0072 | 0x0073 | 0x0074 | 0x0075 | 0x0076 | 0x0077 | 0x0078 | 0x0079 | 0x007A | 0x007B | 0x007C | 0x007D | 0x007E | 0x007F | 0x0080 | 0x0081 | 0x0082 | 0x0083 | 0x0084 | 0x0085 | 0x0086 | 0x0087 | 0x0088 | 0x0089 | 0x008A | 0x008B | 0x008C | 0x008D | 0x008E | 0x008F | 0x0090 | 0x0091 | 0x0092 | 0x0093 | 0x0094 | 0x0095 | 0x0096 | 0x0097 | 0x0098 | 0x0099 | 0x009A | 0x009B | 0x009C | 0x009D | 0x009E | 0x009F | 0x00A0 | 0x00A1 | 0x00A2 | 0x00A3 | 0x00A4 | 0x00A5 | 0x00A6 | 0x00A7 | 0x00A8 | 0x00A9 | 0x00AA | 0x00AB | 0x00AC | 0x00AD | 0x00AE | 0x00AF | 0x00B0 | 0x00B1 | 0x00B2 | 0x00B3 | 0x00B4 | 0x00B5 | 0x00B6 | 0x00B7 | 0x00B8 | 0x00B9 | 0x00BA | 0x00BB | 0x00BC | 0x00BD | 0x00BE | 0x00BF | 0x00C0 | 0x00C1 | 0x00C2 | 0x00C3 | 0x00C4 | 0x00C5 | 0x00C6 | 0x00C7 | 0x00C8 | 0x00C9 | 0x00CA | 0x00CB | 0x00CC | 0x00CD | 0x00CE | 0x00CF | 0x00D0 | 0x00D1 | 0x00D2 | 0x00D3 | 0x00D4 | 0x00D5 | 0x00D6 | 0x00D7 | 0x00D8 | 0x00D9 | 0x00DA | 0x00DB | 0x00DC | 0x00DD | 0x00DE | 0x00DF | 0x00E0 | 0x00E1 | 0x00E2 | 0x00E3 | 0x00E4 | 0x00E5 | 0x00E6 | 0x00E7 | 0x00E8 | 0x00E9 | 0x00EA | 0x00EB | 0x00EC | 0x00ED | 0x00EE | 0x00EF | 0x00F0 | 0x00F1 | 0x00F2 | 0x00F3 | 0x00F4 | 0x00F5 | 0x00F6 | 0x00F7 | 0x00F8 | 0x00F9 | 0x00FA | 0x00FB | 0x00FC | 0x00FD | 0x00FE | 0x00FF |
Actual:
0x0A00 | 0x0A01 | 0x0A02 | 0x0A03 | 0x0A04 | 0x0A05 | 0x0A06 | 0x0A07 | 0x0A08 | 0x0A09 | 0x0A0A | 0x0A0B | 0x0A0C | 0x0A0D | 0x0A0E | 0x0A0F | 0x0A10 | 0x0A11 | 0x0A12 | 0x0A13 | 0x0A14 | 0x0A15 | 0x0A16 | 0x0A17 | 0x0A18 | 0x0A19 | 0x0A1A | 0x0A1B | 0x0A1C | 0x0A1D | 0x0A1E | 0x0A1F | 0x0A20 | 0x0A21 | 0x0A22 | 0x0A23 | 0x0A24 | 0x0A25 | 0x0A26 | 0x0A27 | 0x0A28 | 0x0A29 | 0x0A2A | 0x0A2B | 0x0A2C | 0x0A2D | 0x0A2E | 0x0A2F | 0x0A30 | 0x0A31 | 0x0A32 | 0x0A33 | 0x0A34 | 0x0A35 | 0x0A36 | 0x0A37 | 0x0A38 | 0x0A39 | 0x0A3A | 0x0A3B | 0x0A3C | 0x0A3D | 0x0A3E | 0x0A3F | 0x0A40 | 0x0A41 | 0x0A42 | 0x0A43 | 0x0A44 | 0x0A45 | 0x0A46 | 0x0A47 | 0x0A48 | 0x0A49 | 0x0A4A | 0x0A4B | 0x0A4C | 0x0A4D | 0x0A4E | 0x0A4F | 0x0A50 | 0x0A51 | 0x0A52 | 0x0A53 | 0x0A54 | 0x0A55 | 0x0A56 | 0x0A57 | 0x0A58 | 0x0A59 | 0x0A5A | 0x0A5B | 0x0A5C | 0x0A5D | 0x0A5E | 0x0A5F | 0x0A60 | 0x0A61 | 0x0A62 | 0x0A63 | 0x0A64 | 0x0A65 | 0x0A66 | 0x0A67 | 0x0A68 | 0x0A69 | 0x0A6A | 0x0A6B | 0x0A6C | 0x0A6D | 0x0A6E | 0x0A6F | 0x0A70 | 0x0A71 | 0x0A72 | 0x0A73 | 0x0A74 | 0x0A75 | 0x0A76 | 0x0A77 | 0x0A78 | 0x0A79 | 0x0A7A | 0x0A7B | 0x0A7C | 0x0A7D | 0x0A7E | 0x0A7F | 0x0A80 | 0x0A81 | 0x0A82 | 0x0A83 | 0x0A84 | 0x0A85 | 0x0A86 | 0x0A87 | 0x0A88 | 0x0A89 | 0x0A8A | 0x0A8B | 0x0A8C | 0x0A8D | 0x0A8E | 0x0A8F | 0x0A90 | 0x0A91 | 0x0A92 | 0x0A93 | 0x0A94 | 0x0A95 | 0x0A96 | 0x0A97 | 0x0A98 | 0x0A99 | 0x0A9A | 0x0A9B | 0x0A9C | 0x0A9D | 0x0A9E | 0x0A9F | 0x0AA0 | 0x0AA1 | 0x0AA2 | 0x0AA3 | 0x0AA4 | 0x0AA5 | 0x0AA6 | 0x0AA7 | 0x0AA8 | 0x0AA9 | 0x0AAA | 0x0AAB | 0x0AAC | 0x0AAD | 0x0AAE | 0x0AAF | 0x0AB0 | 0x0AB1 | 0x0AB2 | 0x0AB3 | 0x0AB4 | 0x0AB5 | 0x0AB6 | 0x0AB7 | 0x0AB8 | 0x0AB9 | 0x0ABA | 0x0ABB | 0x0ABC | 0x0ABD | 0x0ABE | 0x0ABF | 0x0AC0 | 0x0AC1 | 0x0AC2 | 0x0AC3 | 0x0AC4 | 0x0AC5 | 0x0AC6 | 0x0AC7 | 0x0AC8 | 0x0AC9 | 0x0ACA | 0x0ACB | 0x0ACC | 0x0ACD | 0x0ACE | 0x0ACF | 0x0AD0 | 0x0AD1 | 0x0AD2 | 0x0AD3 | 0x0AD4 | 0x0AD5 | 0x0AD6 | 0x0AD7 | 0x0AD8 | 0x0AD9 | 0x0ADA | 0x0ADB | 0x0ADC | 0x0ADD | 0x0ADE | 0x0ADF | 0x0AE0 | 0x0AE1 | 0x0AE2 | 0x0AE3 | 0x0AE4 | 0x0AE5 | 0x0AE6 | 0x0AE7 | 0x0AE8 | 0x0AE9 | 0x0AEA | 0x0AEB | 0x0AEC | 0x0AED | 0x0AEE | 0x0AEF | 0x0AF0 | 0x0AF1 | 0x0AF2 | 0x0AF3 | 0x0AF4 | 0x0AF5 | 0x0AF6 | 0x0AF7 | 0x0AF8 | 0x0AF9 | 0x0AFA | 0x0AFB | 0x0AFC | 0x0AFD | 0x0AFE | 0x0AFF |
What am I doing wrong?
Assembly:
cseg
?59999:
defb 48,120,37,48,52,120,32,124,32,0
main#:
ld c,0
#0:
push bc
push bc
ld bc,?59999
push bc
ld hl,2
call printf
pop bc
pop bc
pop bc
inc c
jp nz,#0
ret
public main#
extrn printf
end
Golly. LONG time since I used a z80 C compiler, and most were buggy as [unprintable] back then.
I would suggest that you dump the assembler if the compiler allows. My GUESS is that internally the char is being promoted to a 16 bit INT with indeterminate upper bits set.
The problem is that %04X expects an integer - not a char.
You might try forcing the compiler to play nice by explicitly casting the char to an int - i.e.
printf("0x%04x | ", (int) i);
Most probable thing is that, as being an old 8 bit compiler, it is not converting the char typed i variable into an int and it is just pushing the bc register (assuming your function will not use the high part, which is simply not true, as your function (printf()) expects a whole int as parameter) which you don't know what it has in the b register. The compiler is using the full bc register to print, as you use %x format, which is for an int parameter, and this explains the presence of the high byte as 0x0a in the output (and which doesn't appear anywhere in your assembler listing). Later versions of the standard begun to convert every short and char arguments to int in order probably to avoid this kind of issue.
Try this code, and see if that solves the problem.
#include<stdio.h>
main()
{
char i = 0;
do
{
printf("0x%04x | ", (int) i);
} while (++ i);
}
(I cannot check here, as I have z80 computer, but not a C compiler for it)
Edit
After checking the assembler code, the compiler output just pushes the complete bc register into the stack, in which the lower part (thec register) comes from the character you want to print, but the b register was previously loaded with the high byte of the 59999 pointer to the array of characters of the format string, which happens to be 0xea. So, I got stranged at the output, that should be probably 0xea00, 0xea01, 0xea02, ... and not the output you have. Have you recompiled the source to get the assembler output and the output refers to a different compilation?
To dig a little more I'd need the code of the printf() function, which I assume you don't have. But that seems that converting the parameter to (int) before passing it to the printf() function should solve the problem.

Correct way to join two double linked list

In the Linux kernel source, the list_splice is implemented with __list_splice:
static inline void __list_splice(const struct list_head *list,
struct list_head *prev,
struct list_head *next)
{
struct list_head *first = list->next; // Why?
struct list_head *last = list->prev;
first->prev = prev;
prev->next = first;
last->next = next;
next->prev = last;
}
Isn't the list already pointing to the head of a linked list?
Why do we need to fetch list->next instead?
The double linked list API in the Linux kernel is implemented as an abstraction of circular list. In that simple scheme the HEAD node does not contain any payload (data) and used explicitly to keep starting point of the list. Due to such design it's really simple to a) check if the list is empty, and b) debug list because unused nodes have been assigned to the so called POISON — magic number specific only to the list pointers in the entire kernel.
1) non-initialized list
+-------------+
| HEAD |
| prev | next |
|POISON POISON|
+-------------+
2) empty list
+----------+-----------+
| | |
| | |
| +------v------+ |
| | HEAD | |
+---+ prev | next +----+
| HEAD HEAD |
+-------------+
3) list with one element
+--------------+--------------+
| | |
| | |
| +------v------+ |
| | HEAD | |
| +---+ prev | next +--+ |
| | |ITEM1 ITEM1| | |
| | +-------------+ | |
| +--------------------+ |
| | |
| +------v------+ |
| | ITEM1 | |
+-------+ prev | next +-------+
| DATA1 |
+-------------+
4) two items in the list
+----------+
| |
| |
| +------v------+
| | HEAD |
+------+ prev | next +----+
| | |ITEM2 ITEM1| |
| | +-------------+ |
+----------------------------+
| | | |
| | | +------v------+
| | | | ITEM1 |
| | +---+ prev | next +----+
| | | | DATA1 | |
| | | +-------------+ |
| +-------------------------+
| | |
| | +------v------+
| | | ITEM2 |
+---------+ prev | next +----+
| | DATA2 | |
| +-------------+ |
| |
+----------------------+
In the lock less algorithm there is a guarantee only for next pointer to be consistent. The guarantee wasn't always the case. The commit 2f073848c3cc introduces it.

How to show several loops and doing stuff in it in a sequence diagramm?

I want to show what my UserControl/Control is doing when I plug a list of data in it, what happens when the user press certain keys, selecting text etc...
I feel somehow a sequence diagramm is not really suited for showing several loops and doing stuff within the loops.
Am I wrong or how can I cope with that case?
If you are talking about a loop, then you have a series of operations that take place for all elements in the loop.
I would model the operations done in the loop as a sequence diagram by itself, if the operation in the loop are fairly complex.
I don't think we can have rules of thumb here, but when the process with the loop itself is complex, and the loop is relatively less complex, we can have them in a single sequence diagram.
If the process that has the loop is not very complex, but the loop is complex, then I would draw a sequence diagram for the operations of the loop and have a note that this entire sequence is called by a loop.
You can also have both sequence diagrams if needed.
Update:
We have to add some notes to the diagram because it is not straightforward to denote a "condition" in a sequence diagram.
The validate is part is something like
do validation
if validation succeeds
proceed to next (business or other) logic
if validation fails
feedback to user (or some other logic)
+----+ +----+ +----------------+ +----------------+
|User| | UI | | Your Validator | | Business Logic |
+----+ +----+ +----------------+ +----------------+
| select | | |
|--------------->| doValidation | |
| |------------------>|----+ |
| | | | Validate |
| | |<---+ |
| | | |
| | | (validation fails: |
| | Validation Fail | feedback to client) |
| |<------------------| |
| | | |
| | | |
| | | (validation succeeds: |
| | | proceed to |
| | | business logic) |
| | | |
| | | someLogic |
| | |----------------------->|
| | | |
UPDATE 2
Why use sequence diagram in a case as mine?
Because you still have to show the sequence of operations, and the developer still needs this information for coding :-)
With UML, as you probably already know, nothing is imposed. You are at your freedom to denote something in some fashion, provided your team also understands it the way you intended. These notes are also helpful.
I should have mentioned this before, some use an "option" fragment to denote a if else. This is more or less a note (I see it this way) but is perhaps more evident. I use them only when both the IF and the ELSE parts are both complex.
+----+ +----+ +----------------+ +----------------+
|User| | UI | | UI - Backend | | Busines Logic |
+----+ +----+ +----------------+ +----------------+
| Add Record | | |
|--------------->| doinsertOrUpdate | |
| |------------------>| |
| | | exists(record) |
| | |----------------------->|
| | | |
____|________________|___________________|________________________|__________
|[Record exists] | | | |
| | | | Get Record | |
| | | |----------------------->| |
| | | | | |
| | | |--------+ | |
| | | | | Set UI Values | |
| | | |<-------+ | |
| | | | | |
| | | | Update Record | |
| | | |----------------------->| |
| | | | | |
| | | Send Message | | |
| | |<------------------| | |
| | | "Record found, | | |
| | | Updated" | | |
|___|________________|___________________|________________________|_________|
| | | |
| | | |
______|________________|___________________|________________________|_________
| [Record does not | | | |
| exist] | | | |
| | | |--------+ | |
| | | | | Generate | |
| | | | | Seqeuence | |
| | | |<-------+ | |
| | | | | |
| | | | Create New Record | |
| | | |----------------------->| |
| | | Send Message | | |
| | |<------------------| | |
| | | "New Record | | |
| | | Created" | | |
|_____|________________|___________________|________________________|_________|
| | | |
| | | |
| | | |
See this for an example using an alt block.

How do I find the most “Naturally" direct route using A-star (A*)

I have implemented the A* algorithm in AS3 and it works great except for one thing.
Often the resulting path does not take the most “natural” or smooth route to the target.
In my environment the object can move diagonally as inexpensively as it can move horizontally or vertically.
Here is a very simple example; the start point is marked by the S, and the end (or finish) point by the F.
| | | | | | | | | |
|S| | | | | | | | |
x| | | | | | | | | |
x| | | | | | | | | |
x| | | | | | | | | |
x| | | | | | | | | |
x| | | | | | | | | |
|F| | | | | | | | |
| | | | | | | | | |
| | | | | | | | | |
As you can see, during the 1st round of finding, nodes [0,2], [1,2], [2,2] will all be added to the list of possible node as they all have a score of N.
The issue I’m having comes at the next point when I’m trying to decide which node to proceed with. In the example above I am using possibleNodes[0] to choose the next node. If I change this to possibleNodes[possibleNodes.length-1] I get the following path.
| | | | | | | | | |
|S| | | | | | | | |
| |x| | | | | | | |
| | |x| | | | | | |
| | | |x| | | | | |
| | |x| | | | | | |
| |x| | | | | | | |
|F| | | | | | | | |
| | | | | | | | | |
| | | | | | | | | |
And then with possibleNextNodes[Math.round(possibleNextNodes.length / 2)-1]
| | | | | | | | | |
|S| | | | | | | | |
|x| | | | | | | | |
x| | | | | | | | | |
x| | | | | | | | | |
x| | | | | | | | | |
x| | | | | | | | | |
|F| | | | | | | | |
| | | | | | | | | |
| | | | | | | | | |
All these paths have the same cost as they all contain the same number of steps but, in this situation, the most sensible path would be as follows...
| | | | | | | | | |
|S| | | | | | | | |
|x| | | | | | | | |
|x| | | | | | | | |
|x| | | | | | | | |
|x| | | | | | | | |
|x| | | | | | | | |
|F| | | | | | | | |
| | | | | | | | | |
| | | | | | | | | |
Is there a formally accepted method of making the path appear more sensible rather than just mathematically correct?
You need to add a Tie-breaker to your heuristic function. The problem here is that there are many paths with the same costs.
For a simple Tie-breaker that favors the direct route you can use the cross-product. I.e. if S is the start and E is the end, and X is the current position in the algorithm, you could calculate the cross-products of S-E and X-E and add a penalty to the heuristic the further it deviates from 0 (= the direct route).
In code:
dx1 = current.x - goal.x
dy1 = current.y - goal.y
dx2 = start.x - goal.x
dy2 = start.y - goal.y
cross = abs(dx1*dy2 - dx2*dy1)
heuristic += cross*0.001
See also http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html#S12, which is an excellent tutorial about A* in general.
If you want paths that look natural, you need to make sure that your costs correspond to the length on a cartesian coordinate system. That means the cost of moving diagonally should be sqrt(2) times the cost of moving vertically or horizontally.
You can add 'control effort' to the cost calculations for each square. The actor will try not to turn or change direction too much as that will add a cost to the path:
http://angryee.blogspot.com/2009/03/better-pathfinding.html
If I remember correctly, the trick to this is to add an extra parameter to the cost function (for every step between adjacent nodes, or squares in your case) that penalises turns slightly more than normal (for example, having a relative cost of greater than sqrt(2) for digonal moves). Now, there's probably a fine line between smoothing out the path and actually decreasing the optimality of the route (elongating it), however, and you're not going to be able to avoid this in any way. There's a certain trade-off you'll need to discover specific to your own application, and this can only really be achieved by testing.
There was an article on a game dev site, I believe, that detailed exactly how this could be done, but I can't seem to find it at the moment. Have a play around with your cost function anyway and see what results you get - I'm pretty sure that's the way to go.
What is more 'sensible'? Straighter? You need to quantify it properly if the algorithm is going to do anything about it.
Since moving diagonally is as inexpensive as moving horizontally/vertically, all the paths are equivalent according to all the criterion available to A*. If you want a more 'sensible' path, you need to tell the algorithm that some paths are more desirable than others, effectively weighting horizontal/vertical as 'better' than diagonal. As far as I can see, that would be altering the parameters of your environment.

Resources