24 May, 2009, boblinski wrote in the 1st comment:
Votes: 0
Alright, I'm trying to add a "scout" skill to the mud, using the pre-made code in scan.c (QuickMUD).. this is the pre-made stuff.. (it must stay unchanged).
void scan_list (ROOM_INDEX_DATA * scan_room, CHAR_DATA * ch, sh_int depth,
sh_int door)
{
CHAR_DATA *rch;

if (scan_room == NULL)
return;
for (rch = scan_room->people; rch != NULL; rch = rch->next_in_room)
{
if (rch == ch)
continue;
if (!IS_NPC (rch) && rch->invis_level > get_trust (ch))
continue;
if (can_see (ch, rch))
scan_char (rch, ch, depth, door);
}
return;
}

void scan_char (CHAR_DATA * victim, CHAR_DATA * ch, sh_int depth, sh_int door)
{
extern char *const dir_name[];
extern char *const distance[];
char buf[MAX_INPUT_LENGTH], buf2[MAX_INPUT_LENGTH];

buf[0] = '\0';

strcat (buf, PERS (victim, ch));
strcat (buf, ", ");
sprintf (buf2, distance[depth], dir_name[door]);
strcat (buf, buf2);
strcat (buf, "\n\r");

send_to_char (buf, ch);
return;
}


Here is the code I am trying to write:
void do_scout (CHAR_DATA * ch, char *argument)
{
ROOM_INDEX_DATA *scan_room;
EXIT_DATA *pExit;
sh_int door, depth, shallow;

if (ch->in_room->sector_type == SECT_FOREST
|| ch->in_room->sector_type == SECT_CITY)
{
shallow = UMIN((ch->level / 13) + 2, 5);
}
else
{
shallow = UMIN ((ch->level + 1) / 8, 3);
}

act ("$n scouts the area quickly", ch, NULL, NULL, TO_ROOM);
send_to_char ("You scout the area and see:\n\r", ch);

scan_room = ch->in_room;

for (depth = 1; depth < shallow; depth++)
{
for (door = 0; door < 6; door++)
{
if ((pExit = scan_room->exit[door]) != NULL)
{
scan_room = pExit->u1.to_room;
scan_list (pExit->u1.to_room, ch, depth, door);
}
}

}
return;
}


The problem is..
In my room: the cityguard
1 east: the guardian vampire
2 east: a patrolman
3 east: an ogre gang member
4 east: a troll gang member
1 west: the kitten
(nothing else west)

so.. my "scout should show:
Quote
You scout the area and see:
the guardian vampire, nearby to the east.
a patrolman, not far east.
an ogre gang member, off in the distance east.
a troll gang member, way WAY east
the kitten, nearby to the west.


But instead I get:
Quote
You scout the area and see:
the guardian vampire, nearby to the east.
a patrolman, not far east.
an ogre gang member, off in the distance east.
a troll gang member, way WAY east
an ogre gang member, nearby to the west.
a patrolman, not far west.
the guardian vampire, off in the distance west.
the cityguard, way WAY west


It seems to be looking east and then moving me to that furtherest east room and once there looking back west…
25 May, 2009, Sharmair wrote in the 2nd comment:
Votes: 0
You have your loops in the wrong order. You should be looping for door direction on
the outside loop, and have your loop for distance on the inside (then the if inside that).
The scan_room = ch->in_room; should also be in the outer loop. And your shallow
better not EVER be more that whatever you have the distance[] dimensioned as.
for(door = 0; door < 6; ++door){
scan_room = ch->in_room;
for(depth = 1; depth < shallow; ++depth){
if((pExit = scan_room->exit[door]) != NULL){
scan_room = pExit->u1.to_room;
scan_list (pExit->u1.to_room, ch, depth, door);
}else
break;
}
}

What you want to do is for each direction look as far as you can and output based on
that distance and direction.
25 May, 2009, boblinski wrote in the 3rd comment:
Votes: 0
only had time to test it really quick, but it looks great, thanks!
25 May, 2009, boblinski wrote in the 4th comment:
Votes: 0
Does anyone have a clue how I would change the display of my scout–

From:
Quote
You scout the area and see:
the guardian vampire, nearby to the east.
a troll gang member, nearby to the east
a patrolman, not far east.
an ogre gang member, off in the distance east.
the kitten, nearby to the west.


To:
Quote
You scout the area and see:
Eastwards: the guardian vampire, nearby
a troll gang member, nearby
a patrolman, not far
an ogre gang member, off in the distance
Westwards: the kitten, nearby
25 May, 2009, boblinski wrote in the 5th comment:
Votes: 0
I've been having a play around by myself.. I've gotten most of the way…

This is my new scan_char():
void scan_char (CHAR_DATA * victim, CHAR_DATA * ch, sh_int depth, sh_int door)
{
extern char *const dir_name[];
extern char *const distance[];
char buf[MAX_INPUT_LENGTH], buf2[MAX_INPUT_LENGTH];

buf[0] = '\0';

if (door < 1)
sprintf (buf, "Northwards: ");
else if (door < 2)
sprintf (buf, "Eastwards: ");
else if (door < 3)
sprintf (buf, "Southwards: ");
else if (door < 4)
sprintf (buf, "Westwards: ");
else if (door < 5)
sprintf (buf, "Upwards: ");
else
sprintf (buf, "Downwards: ");

if (IS_NPC(victim))
strcat (buf, victim->short_descr);
else
strcat (buf, victim->name);

strcat (buf, ", ");
strcat (buf, distance[depth]);
strcat (buf, "\n\r");

send_to_char (buf, ch);
return;
}


Now when I use my do_scout().. I get this:
Quote
You scout the area and see:
Eastwards: the guardian vampire, nearby
Eastwards: a troll gang member, nearby
Eastwards: a patrolman, not far
Eastwards: an ogre gang member, off in the distance
Westwards: the kitten, nearby


Can't figure out how to stop the extra "Eastwards: "…
25 May, 2009, David Haley wrote in the 6th comment:
Votes: 0
You could test on depth, but that would only work correctly if the first room had somebody in it. The function isn't really set up to do the test the proper way; it's a function to display scan results, as opposed to the function that controls the scan – that is where you'd have to remember when you've already printed the direction.

One thing to try is to first write out your intention in clear and precise English, as if you were speaking to, well, a computer – someone that has no idea what you want and can only execute the most elementary of instructions. That's the best way (if not the only way) to write the code to do what you want.
25 May, 2009, boblinski wrote in the 7th comment:
Votes: 0
What about something like this:

int firstn = 0, firste = 0, firsts = 0, firstw = 0, firstu = 0, firstd = 0;

if (door == 0 && depth == 1)
{
if (victim != NULL)
{
firstn = 1;
}
}
else if (door == 1 && depth == 1)
{
if (victim != NULL)
{
firste = 1;
}
}
else if (door == 2 && depth == 1)
{
if (victim != NULL)
{
firsts = 1;
}
}
else if (door == 3 && depth == 1)
{
if (victim != NULL)
{
firstw = 1;
}
}
else if (door == 4 && depth == 1)
{
if (victim != NULL)
{
firstu = 1;
}
}
else if (door == 5 && depth == 1)
{
if (victim != NULL)
{
firstd = 1;
}
}
//next
if (door == 0 && depth != 1)
{
if (firstn == 0 && victim != NULL)
{
firstn = 1;
}
}
else if (door == 1 && depth != 1)
{
if (firste == 0 && victim != NULL)
{
firste = 1;
}
}
else if (door == 2 && depth != 1)
{
if (firsts == 0 && victim != NULL)
{
firsts = 1;
}
}
else if (door == 3 && depth != 1)
{
if (firstw == 0 && victim != NULL)
{
firstw = 1;
}
}
else if (door == 4 && depth != 1)
{
if (firstu == 0 && victim != NULL)
{
firstu = 1;
}
}
else if (door == 5 && depth != 1)
{
if (firstd == 0 && victim != NULL)
{
firstd = 1;
}
}


Do you think a system like that might work?

How do I add a variable that can be called/used/edited in all of the functions (ie, scan_char(), do_scout() and scan_list()) ?
26 May, 2009, David Haley wrote in the 8th comment:
Votes: 0
It's a little redundant; you're basically testing the exact same thing but in two different ways.

I still think you should try to write out in English what exactly you're trying to achieve; once we get that sorted out we can start talking code.
26 May, 2009, Sandi wrote in the 9th comment:
Votes: 0
I agree with David that some planning is in order.

And, while you could make one long contraption with all sorts of mechanisms to do this, but not that, and only if…. I think this might be the place to write a lot of little functions that you call as needed. Also, remember you can send each line separately - you don't need to collect them all in one big buf you send at the end.
28 May, 2009, boblinski wrote in the 10th comment:
Votes: 0
Basically I want to have a "scan" command and a "scout" skill..
———————————–

Scan:
Target specific (scan north, south, east, west, down or up).

Distance of scan varies depending on class and level (max distance of 4 rooms).

Example Input/Output:
Quote
>scan east
Eastwards: a dog, nearby.
a farmer, nearby.
the farmers daughter, not far away.
a race horse, in the distance.
working man, barely visible.

———————————–

Scout:
A scan of all directions.

Distance of scout varies depending on class and level (max scout-distance of 3 rooms).

Example Input/Output:
Quote
>scout
Northwards: farmers wife, nearby.
billy goat, in the distance.
Eastwards: a dog, nearby.
a farmer, nearby.
the farmers daughter, not far away.
a race horse, in the distance.
working man, barely visible.
Westwards: a cow, not far away.
a donkey, not far away.
farm helper, off in the distance.
Upwards: giant eagle, off in the distance.

———————————–

Note:
1 room away: nearby.
2 room away: not far away.
3 room away: in the distance.
4 room away: barely visible.
28 May, 2009, David Haley wrote in the 11th comment:
Votes: 0
OK, it's a good first step to plan out at a high level what you want things to do. Next, you need to plan out the actual logic of your code, but without actually writing code. Imagine you had to explain how to implement 'scout' using pen and paper to somebody who knew nothing about anything. You'd have to say things like: if this is the first thing we've seen in this direction, print out the direction name, otherwise just print out an indent.

It's absolutely critical that you be able to explain your code in English. If you can't say it in a language you understand well, there's no hope you'll be able to say it in a language you're only just starting to learn. It's not just explaining what the output is supposed to look like: you have to be able to express the algorithm.

A good exercise for this is to take algorithms that you understand very well and explain them as if you were talking to a ten year old. For instance, try explaining how to implement long division. Or, explain how to compute the remainder of a division. Whatever it is, just pick something that you understand well, and lay out all the steps so clearly that somebody completely ignorant of the subject matter could follow you.

I cannot stress enough how important this is: it's not just some useless exercise I'm putting you through just because I feel like it. You can't write code effectively unless you can decompose your solution into clear, simple steps.
28 May, 2009, Runter wrote in the 12th comment:
Votes: 0
Here's you a little pseudocode to get you on your way.

start Scan
exit = rooms.exit_list
people = NULL

do Rooms loop while (exit is valid)
people = exit.people_list

do People loop while (people is valid)
print people.name
people = get_next_person(people)
end do
exit = get_next_exit(exit)
end do

stop Scan


Of course to do what you are wanting is a little more than that, but that should get you started and your gears turning on how to make the modifications yourself.
28 May, 2009, Tyche wrote in the 13th comment:
Votes: 0
3-2
| |
X-1-2-3

So what if you are at X and scan east to a depth of 3.
Do you see every room above?

So what if you are at X and scan east to a depth of 4.
Do you see yourself in room X in the "far distance"?
28 May, 2009, Runter wrote in the 14th comment:
Votes: 0
Some people use logical exits.
28 May, 2009, Sandi wrote in the 15th comment:
Votes: 0
I have a 'dowse' skill that looks around corners, and in Tyche's example it was a lot of fun designing the output for the upper room 2.

boblinski, if you make 'scan' a command, people will simply make a function key send 'scan n;scan e;scan s;scan w', and not need the skill.
29 May, 2009, boblinski wrote in the 16th comment:
Votes: 0
Scan east would see 1, 2, 3 & 4 for a high level ranger in a forest or a high level thief in a city.
Otherwise a high level non ranger or thief would see only 1 and 2.
0-0-0-0-0-0-0-0-0
| | | | | | | | |
0-0-0-0-X-1-2-3-4
| | | | | | | | |
0-0-0-0-0-0-0-0-0


Scout will be given to thieves and rangers only as a -skill- and will show -all- 1's and 2's (and 3's and 4's for rangers in forests or thieves in cities).
0-0-0-0-4-0-0-0-0
| | | | | | | | |
0-0-0-0-3-0-0-0-0
| | | | | | | | |
0-0-0-0-2-0-0-0-0
| | | | | | | | |
0-0-0-0-1-0-0-0-0
| | | | | | | | |
4-3-2-1-X-1-2-3-4
| | | | | | | | |
0-0-0-0-1-0-0-0-0
| | | | | | | | |
0-0-0-0-2-0-0-0-0
| | | | | | | | |
0-0-0-0-3-0-0-0-0
| | | | | | | | |
0-0-0-0-4-0-0-0-0
29 May, 2009, boblinski wrote in the 17th comment:
Votes: 0
Tyche said:
3-2
| |
X-1-2-3

So what if you are at X and scan east to a depth of 3.
Do you see every room above?


Firstly, depth isn't chosen by a character, it is decided on by the code based on level, class and surroundings.
In the example, I want only the rooms directly east of 'X' to be scanned..

Tyche said:
So what if you are at X and scan east to a depth of 4.
Do you see yourself in room X in the "far distance"?


I want the code to recognize that… even though 'X' is trying to scan to a depth of 4.. there are only 3 rooms, so stop scanning in room 3.
29 May, 2009, boblinski wrote in the 18th comment:
Votes: 0
THE SCAN FUNCTION DRAFT

If a player is blind.. stop.

If no argument given.. ask for one.

Grab the direction.. n/north=0, e/east=1 etc
If the direction doesn't equal 0-5 ask for direction again.

Get the distance of scan based on level and class.
If they are a ranger or a thief distance is "(level/13)+1"
For non rangers and thieves.. if "level < 25" distance is 1.. otherwise distance=2.

Go 1 room in 'direction' and get a list of all the NPCs & PCs. (if there is no room in 'direction'… stop)
For each target.. make sure..
ch is not target
ch can_see target

Then.. print the targets in a list:
If it's the first target for the 'direction' print "Northwards: " or "Eastwards: " etc depending on 'direction'. Followed by targets name (or short desc for NPC). Followed by ", nearby." then finish line.
If it's not the first target for 'direction' print 12 spaces. Followed by targets name. Followed by ", nearby.". Then finish line.

Now, check to see if distance is more then 1.. if so..

Go 1 MORE room in 'direction' and get a list of all NPCs & PCs. (if there is no room in 'direction'… stop)
For each target.. make sure..
ch is not target
ch can_see target

Then.. print the targets in a list:
If it's the first target for the 'direction' print "Northwards: " or "Eastwards: " etc depending on 'direction'. Followed by targets name (or short desc for NPC). Followed by ", not far away." then finish line.
If it's not the first target for 'direction' print 12 spaces. Followed by targets name. Followed by ", not far away.". Then finish line.

Now, check to see if distance is more then 2.. if so..

Go 1 MORE room in 'direction' and get a list of all NPCs & PCs. (if there is no room in 'direction'… stop)
For each target.. make sure..
ch is not target
ch can_see target

Then.. print the targets in a list:
If it's the first target for the 'direction' print "Northwards: " or "Eastwards: " etc depending on 'direction'. Followed by targets name (or short desc for NPC). Followed by ", in the distance." then finish line.
If it's not the first target for 'direction' print 12 spaces. Followed by targets name. Followed by ", in the distance.". Then finish line.

Now, check to see if distance is more then 3.. if so..

Go 1 MORE room in 'direction' and get a list of all NPCs & PCs. (if there is no room in 'direction'… stop)
For each target.. make sure..
ch is not target
ch can_see target

Then.. print the targets in a list:
If it's the first target for the 'direction' print "Northwards: " or "Eastwards: " etc depending on 'direction'. Followed by targets name (or short desc for NPC). Followed by ", barely visible." then finish line.
If it's not the first target for 'direction' print 12 spaces. Followed by targets name. Followed by ", barely visible.". Then finish line.
29 May, 2009, David Haley wrote in the 19th comment:
Votes: 0
OK, this is very good, this is what I was hoping you would do. Some comments:

Quote
Go 1 room in 'direction' and get a list of all the NPCs & PCs. (if there is no room in 'direction'… stop)
For each target.. make sure..
ch is not target
ch can_see target

Assuming that you don't have looping exits, where going east can land you back at the current position, note that you don't actually need to check that target != ch because the room will be different. If you can't make that assumption, then you are correct that you need to check that ch != target. (Same holds for all other similar checks, of course.)

Quote
If it's the first target for the 'direction' print "Northwards: " or "Eastwards: " etc depending on 'direction'. (…….)

Look at the structure of what you're doing: there is a pattern here that you can take advantage of. For each target in the list of people in the room, you are printing something like this:
<prefix> <target name> <suffix>

where:
- prefix is "<x>wards:" if this is the first match, and 12 spaces if this is not the first match
- target name is the name of the target (easy enough)
- suffix is ", nearby", ", in the distance", etc. depending on the distance from the starting room.


You have the general structure right for the algorithm. Let's move from English to pseudo-code. I'll re-write your English into pseudo-code, and incorporate the above comments.

function do_scan(ch, argument)
if ch is blind: return
if argument == "":
send to ch: "you need an argument"
return
direction = str_to_direction(argument) // see below
if direction == -1:
send to ch: argument + " is not a valid direction"
return

if ch is ranger or ch is thief:
max_distance = 1 + (ch->level / 13)
else:
max_distance = ch->level < 25 ? 1 : 2

cur_room = ch->room
next_room = get cur_room->exits[direction]
if next_room is null:
send to ch: "no exit to the " + argument
return

// start scanning, with:
// actor: ch
// room: next_room (== cur_room->exits[direction])
// direction: direction
// current depth: 1
// max_depth: max_depth
// have we seen somebody: FALSE
scan(ch, next_room, direction, 1, max_depth, FALSE)
end


Note that we're cutting things up into nice pieces here. The function str_to_direction takes a string (argument) and compares it against known directions ("north", "south", …) and returns the appropriate integer. Doing it this way has two advantages:
(1) you can reuse the code in other functions, like, say, your "scout" function
(2) it makes the do_scan command cleaner and therefore easier to read

Now we have to implement str_to_direction and the actual scan function. I'll leave str_to_direction as an "exercise to the reader" (I've always loved that expression), but scan is the more interesting bit.

function scan(ch, room, direction, cur_depth, max_depth, seen_already)
if cur_depth > max_depth:
return

// we are currently in 'room', and need to display targets to ch
for each 'person' in room->people:
if person == ch: continue
if not(ch can see person): continue

if seen_already:
prefix = 12 spaces
else:
prefix = direction_to_xwards(direction)

suffix = depth_to_distancestr(cur_depth)

send_to_ch: prefix + " " + target->name + " " + suffix
seen_already = TRUE

// we have finished the people in this room. Move on to the next one.
next_room = get room->exits[direction]
if next_room is null:
return
else:
scan(ch, next_room, direction, cur_depth + 1, max_depth, seen_already
end


We still have to implement direction_to_xwards, which converts a direction to e.g. "northwards", and direction_to_distancestr, which converts a depth to a distance, e.g. 1 to "nearby".

Note how simple this function is, especially compared to the long sequence of checks given in post #7. The simplicity was obtained in the following manner:

Step 1: Clearly and precisely identify the exact sequence of steps and logic of your algorithm.
Step 2: Examine the steps you have laid out, and look for patterns.
Step 3: Use patterns to consolidate steps into templates, and fill in values with context.


The 'scan' function I gave uses a technique called recursion, which is where a function calls itself to continue doing the work. In general, recursion is used when you have a problem that you can decompose into the same problem, just smaller or different. In our case, the scan itself works the exact same way no matter how far away you are, it's just that a few details of what you print out get changed. In any case, when we've exceeded our maximum distance, we stop the recursion: that is what that first if statement is for.


Now that you're armed with pseudo-code (disclaimer: I haven't tested it) you can start writing actual code. You should find that it will be much easier to write C code when you have a very clear idea of the sequence of steps to take.

In the future, as you learn to program and write more code, this is a very useful technique when planning out a project. You were able to give a very good list of exact steps to follow for the algorithm. The next thing to do is to take a step back and look critically at your sequence of instructions, and see if you can find patterns, or basically generalize each step into the same sequence with minor details adjusted.

I've said it before but it's important enough to warrant repeating: without a very clear understanding of your task in English, any attempt to write code is going to be very hard or impossible, and at best, the result will be a barely functional but ugly and hard to maintain program.

Hope this helps. :smile:
29 May, 2009, boblinski wrote in the 20th comment:
Votes: 0
Wow, this is so helpful, thanks so much for helping!

So, 2 questions for my str_to_direction() function..

1) Will something like this work?
bool str_to_direction(const char *arg1)
{
else if (!str_cmp (arg1, "n") || !str_cmp (arg1, "north"))
return 0;
else if (!str_cmp (arg1, "e") || !str_cmp (arg1, "east"))
return 1;
else if (!str_cmp (arg1, "s") || !str_cmp (arg1, "south"))
return 2;
else if (!str_cmp (arg1, "w") || !str_cmp (arg1, "west"))
return 3;
else if (!str_cmp (arg1, "u") || !str_cmp (arg1, "up"))
return 4;
else if (!str_cmp (arg1, "d") || !str_cmp (arg1, "down"))
return 5;
else
return -1;
}


2) Do I put this into somewhere like db.c and then declare it in merc.h under /* db.c */ with something like:

bool     str_to_direction            args( ( const char *arg1) );
Random Picks
0.0/44